D^35HE*T&(GWt+r>gdCT
zwXc?A(DWXyvHFWO*t$E&lZMP0o;ht2SJ@vHc6c;m%^iATiYje!0|<*d)-84jbhS;s(yJ(~H(Ss?}<-V&qsX5^h
zkUz)0$8Ohz&5?SiPD?SCo=CIb4_I=q%pGnx+lQ;BABFj@&nxGe^iHfchWw$xeKVAcKuv{`6%Eb}r&i4uEJm;saYJTa+v9JS#xI<1;^eLLk6W(`#$4>%PF_or!tP4S
zOzMu6i2ag#$P|J2V8+^-|6YXcBll_KDsJE2?ygi+fJST#eHrfU&*;xjUrtYXMn7M$
z^||Sb(!H_vmiGNmyMhg1Cs&VdE;Mt-IVwwcrlg!_&Vw#!asB>$u;%uvXfs7AC50=%
zpRw|N$>>J(&L1Y*lE8Lj__8@f76q^%Gkho@TRM?SaitK+A%R^K6aesR(eMN|!O;Op
zV$dPP9Un+AoykK30LmoFV#N-gJ@(l*u%(C-;s%>_|a@aSroTW
zCp;H~Ce~ThG9syw)
zEDFd30@EYu>+6F|5D*HP2=_7YC6aVOaD6xo3gg`-dN30t+yDtffd0O~ynrmSFVYof
z^>;Yl2@UpRvzbUJl*8dbI0g^~iwcDy5D2I~914f)@jUc`LIT*tV7-7KwI2*PN)U-f
zW3p+C0MHI2(T8!CjRy0?{yPgg^B>xPpua1{s~I$y$b`Zm`a4!7}Ki)xX
zSIU3%{#W84d#E-#d1o<(TAh-bp
z2GVjQl4t=t7VWg260t78WKZ
zhY%(hgaHm?Wra2PfyFUMXX%sx_75!iKUl<1u{#4nXYwNBC@k7J3fYRqpo9L(8cF-P
z7UQ4t{f#C6T#Lz1u~42E=uU6{U$1|dcmuSv{bzW2hkwSO62O~z7H_!8UA2pOjBYOr6$rKCie3z7@InH%mQJ`zjCb_^0Yo%bT>t<8
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/CoolLo_hvr_25x27.png b/assets/icons/Infrared/CoolLo_hvr_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..ae5316e4d49992b01a39a4298e36cac1fd365388
GIT binary patch
literal 3657
zcmaJ@c{r478-GRiwJb?GV+e^EGqz@$vCNDuhA=XcGG@k@G>b7cmWaxh4oO)fEwZa9
z63P}z2$5Zdqz=AgiIbRboO3$g_s7@wUhn%p&vRe*?|1+1<-M*q{*aTcjO0#9003m{
z?QpJwRaNk)i3S)G4hgu2bxk2)H3Eg&2d)=;3J3-FN{IvY_^f)vU9o(*&zOv(LXj7Q>bc4L9mFFQ
z0n5-t%w53JMkq2nUE2-F6#;zO$-Wc7Ap^ii=j_K`AToP3BTNYJx~ichl$#8Iwo~J9
zfd2uYr0eiSYk+77DEc`xBY*(|z|h{q&knd>1=M%SOO*rS(tx2$T=ZT*BnI;|8CfiSog|z7rc$UBzrm0C>-h=-$wWR_u&C
zAR5WoxV2|a#Q&I7?x~dozj8UtM?mh(kjEl>vo_W@FINu^PEAe@9BjsTkzRU)Z_pZ#
z*L$p={38POeP#LG(+^rvaPKHvk=1vNua+G1c6D8mxZpeTG0FCOl@$NGdM~)y-nW{l
zr#RuVEie(6d1(=z2ad;F+53D?51;a3Tz^g2eB1=Mf5KcSEZLyIn=Jg*K}Vj*t^cqB
z01Gv&hBvz6!jb;x-*@r&^A_v2xqE;}Kl>|z0B{Va1tGkwuxJ$r09bLDUrK4K6^{fVRU0U?yKwhK;KxZ8`rz|$E2`jA&u^j
zWzlT8ZzS=Y2Hn~y2@$I{@$Dpyp*SJmi$u*tag~hdRgDHak!=ZDpciq9E?|db6FZpe
zMFPm(PJIE**@B2YYV#!dKpOx*_dTZ87IrnN+y(tc`VqFez-;FQn_E(^DO^})l5%d=
zYtF-+=u4(Ir(YNQ%MB%&V_OaDKkX1RYCdTQX=%bd*sm{8)$MJN;GsSLpXXai1uRUfb=I_S&PT
zea7}v+9%;9p0rGi()rdnnyzZr4zV3HRo;$!>5g|*dlOV!35I5{FX_`6av3alqF$xm
z8KvZxlGhGi)4ryr2PG61-7Oj`l5$^kZzm)b9&-yXvvD_an<$iclUnRvlzr%no0=P~
z@IXOq5v_nlu(%W9xgIa-+2q*djJ@NM`{4LQZ3{?>tXdJQuMr?q9CIqlq*?nx$KaXM
zdNt|m>NN@IqQaX9tkFRuj|$htt9}+@6X}7g*SSs-)4KU-ixC&c6zBJT*sFHe*s&T$
zJsMDynwCl|wkviiw*69PS~i=XeYeiK#&a!7)~fZO&QTp(T2klM>}j__x6ypbd}TZu
zxm*S*3dmEtS%c#}W9HAL=R7?Ynd8XH9}B6Q{
OLy)-
zJvRwtq12(R!@s*=y_+ntd8BKkwD%X1tGiLR`)q!-k-J21IXa;=!DwN$+}0M=*3fpq
zf5y*cy5g(v?!9}!@qlA%$E%K89sL<5S!mc>_;l8E*4wP{DxV|4NAxQTDj!qr%O{WA
z^_;7$Wkw%Cc;*q^k9b$ulxCOi>8~lR$ydm)c1m$7&grK$-T%GvMP*@?>iww!(c!X6
zMrAmO=Bq-hdS2Evmr|D!{v`a@b+njF?4WG1`VsYrpobhqb6TS(owW*9JD65)(>j-ox9)`XIw
z$sVO|?^osVidIPTD&}9!IX5QECs1Z%@G;NhugvPJ+N#=}n^-x=qsOslHTe{ND8C!X
zkK7|f6ONCvit^gBwi9UJWtW0A;?Bkt^mrJ3-$N3cQTxNo>r+*lS3=8U*!
zaL`20ya;jvHDvb0_=!ae#Lx3pA2FL~f6`$W0-{%D$~^gW{QK-;?1$_RQWIe+>CiTB
zcF4`n#--?d^NXSxIfiUpR<`w*ryJSxJOde18TVhWw|vlK<3c89RwTw(cFQ1#PG1Tuc`w%c%V~9*
zI`jl4b0O0v%Pn)w%h&6P^DFc2{?B8f-))C~k-Kh;@(ek+NO(aXnmN~-LR;v%lzlBb
zJOc8^h|ln?%CKp2=lBUZ*8CHB?)x5Ve!}#D+5??O7ansL%7>@6dp!2YyoWr__{{x`
z+DPejZmqdqlUY`MG@yDewC+)u-^z?yj#=mEQeDXJ%KZMm90Yo_ZZVX#@_c1^TzNEt
zqqE-GUNgwknJcYzu06hFIcqhD9gXd?@Y)y|m>)TNqMnz#*gayqA{u?ZYa@9%Ne;U;
zDI=*pMkeM<&VF+Q;)4Zeedc>Uwu{oGoujgGYoonPMHw2g+V|ycXLow{&9sHIlxK`H
zdF!9+zo_0DZfR`U{q#k!5$xE~;kDTY-Uv^9{`R<>^W+)OSzW&W9}kw@Ugxi+sHUXw
zMTFBAzt5XoJ-7LX$+aeO-B^BH9*IK*tSBsBD#)HeqETI`BuYr&3n~f#gmvizBA1AF
zM3Pwy2x-#?63k!=&;Wq42xgPW0aPx?mrA2E(ct--XJ8PWf(Cn-;0^I?EY+WG7s{c!
zg*p+)p#fwB1#DpsLIooQ1Pm&d1PW%HWCkIF(cnLMk%IlE843pd>B0>_gZ~mm#2*4-
zSsW_J3<5JC8yXsd%n%SNg#`CC@*|P;L2yGj3A#VanWFb*uS!1u>YZD2K`+rLCv7SBsLTVG2G1Rha(>U|6Li3f4qaZ
zuGIhN{h!1^gb+3r>Pih_o#K!Mz4O!D48=xbIaCst#UZd*Cw~<3kUxvd3i4;ML2x4o
z45W)Ek?G7$%icdBcs$ac8N?+q$y9qB8Z01z(CHMUv5k?H4b~J3-@o4m2D8RlS(%yb
zN0?y{MmUU(4c6!f7RMr=Vo;gfA6UwNu(+RMHwS{j7DUETIrP(1iVcUw0R5RYlKyio
zRzKzY8%z1Q7R*nvP=OfeW^ezi*FQ{x0ovUDGrWSsKjTkj3TB=o81C8WTXeyPA$rJ>
zU@chre16{GjZwjlV2`sR1P_(Cwo3va=^j3RhNz&}`=AN+yqPO^8D~S-qo8Okj|Gl(m`;}p
P_yK!sCtMlE=iI*m`3F!x
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/Dehumidify_25x27.png b/assets/icons/Infrared/Dehumidify_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..dca77ae410bcc2196a693023640372da1b1912ea
GIT binary patch
literal 3665
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
Y0_*BPx59$cJUb46GqS>#p?%qL
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
Tfe|}@hk%WR1FiydCgT49&LB(v
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/HeatHi_25x27.png b/assets/icons/Infrared/HeatHi_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1724f995562587ec54740789f37729d567e0f88
GIT binary patch
literal 3676
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
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/HeatHi_hvr_25x27.png b/assets/icons/Infrared/HeatHi_hvr_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..b92108d68de2ab09bed6b451e36396881e5611ea
GIT binary patch
literal 3661
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
T-|@M}j{(?QJ7Y`Hry~9hE1FGt
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/HeatLo_25x27.png b/assets/icons/Infrared/HeatLo_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..af2e59d4940aefa657c280acb8073800408dabba
GIT binary patch
literal 3670
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
O0dr$(Y#G`u;{O0;Ay2me
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/Off_25x27.png b/assets/icons/Infrared/Off_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..c15100606ac9b2c46fec8d5ed290d72345fb22e2
GIT binary patch
literal 9530
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
T&A>rCE`gSso@%+Wb;y4K-_9ky
literal 0
HcmV?d00001
diff --git a/assets/icons/Infrared/Off_hvr_25x27.png b/assets/icons/Infrared/Off_hvr_25x27.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5e5e6f45d78e070d06e033bd529d0067de94301
GIT binary patch
literal 8460
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
literal 0
HcmV?d00001
diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir
new file mode 100644
index 000000000..b1075b2f3
--- /dev/null
+++ b/assets/resources/infrared/assets/ac.ir
@@ -0,0 +1,38 @@
+Filetype: IR library file
+Version: 1
+#
+name: Off
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 502 3436 510 475 509 476 508 477 507 477 507 479 505 480 504 480 504 490 504 481 502 482 501 483 563 420 511 474 510 475 509 476 508 485 561 423 508 476 508 477 507 478 506 479 505 480 504 481 503 517 508 476 508 478 506 479 505 479 505 481 503 483 521 1456 501 498 507 479 505 480 504 481 503 482 501 483 563 421 562 422 509 499 506 479 505 480 504 481 503 482 502 484 510 1451 506 479 505 1542 562 1396 509 471 502 476 508 469 504 3425 511
+#
+name: Dh
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 507 3430 506 479 505 480 504 481 503 481 503 483 501 485 509 1453 504 1465 503 482 502 483 511 473 500 485 509 476 508 477 507 478 506 487 507 477 507 478 506 479 505 480 504 482 502 483 501 484 500 523 503 482 502 484 500 485 509 476 508 476 508 478 506 1456 501 501 504 482 502 483 501 484 500 485 509 476 508 477 507 1455 502 509 506 479 505 1457 500 485 509 476 508 1454 503 482 502 483 501 568 499 1459 509 1450 507 471 502 474 510 3421 505
+#
+name: Cool_hi
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 504 3433 503 482 502 484 510 474 510 475 509 476 508 478 506 1456 564 1405 510 475 509 476 508 502 482 477 507 478 506 479 505 480 504 489 505 480 504 481 503 482 502 483 511 473 511 474 510 475 509 509 506 479 505 480 504 481 503 482 512 473 511 474 510 476 508 1469 509 475 509 476 508 477 507 478 506 479 505 480 504 481 503 505 510 475 509 502 482 503 481 504 480 505 478 507 477 1459 509 560 507 1451 506 473 511 493 480 1450 507 3422 503
+#
+name: Cool_lo
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 525 3615 530 506 561 474 562 474 562 473 563 473 531 505 562 1502 528 1542 562 474 562 474 530 505 531 504 532 504 532 504 616 419 533 510 589 447 526 509 527 509 527 509 527 508 528 508 528 507 529 542 525 510 526 509 527 509 527 509 527 508 528 508 528 1535 527 524 533 503 533 503 533 502 534 502 534 502 534 501 535 501 525 534 533 502 534 502 534 501 535 502 534 1529 533 503 533 503 533 587 533 497 528 501 524 1536 526 501 524 3609 526
+#
+name: Heat_hi
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 531 3406 530 455 529 456 528 457 537 447 537 448 535 450 534 1429 528 1442 536 448 536 449 534 451 532 452 532 453 530 454 530 455 529 464 530 454 529 456 528 457 537 448 536 449 535 450 533 451 533 490 535 449 534 450 534 451 533 452 532 453 531 455 529 1433 534 1443 535 449 535 450 534 452 531 453 530 454 530 455 529 456 538 472 532 452 532 454 530 1433 535 1427 530 1432 536 1427 530 1431 537 1511 530 448 536 1422 535 1423 534 1422 535 3395 530
+#
+name: Heat_lo
+type: raw
+frequency: 38000
+duty_cycle: 0.33
+data: 506 3430 506 478 506 479 505 480 504 481 503 482 502 484 500 1463 505 1465 503 482 502 483 501 484 500 485 509 476 508 477 507 478 506 486 508 477 507 478 506 479 505 480 504 481 503 482 502 483 500 523 502 482 502 483 501 484 500 485 509 476 508 478 506 1455 502 498 507 478 506 479 505 481 503 482 501 483 500 484 500 485 509 500 505 481 502 482 502 1461 507 1455 502 1459 509 476 508 477 507 563 504 1453 504 1454 503 1454 503 1453 504 3426 499
diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md
new file mode 100644
index 000000000..ac98d451f
--- /dev/null
+++ b/documentation/UniversalRemotes.md
@@ -0,0 +1,36 @@
+# Universal Remotes
+## Air Conditioners
+### Recording signals
+Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
+The majority of A/C remotes have a small display which shows current mode, temperature and other settings.
+When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
+
+In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, `Heat_lo`.
+Each signal (except `Off`) is recorded using the following algorithm:
+
+1. Get the remote and press the **Power Button** so that the display shows that A/C is ON.
+2. Set the A/C to the corresponding mode (see table below), while leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
+3. Press the **POWER** button to switch the A/C off.
+4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
+5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again.
+6. Save the resulting signal under the specified name.
+7. Repeat the steps 2-6 for each signal from the table below.
+
+| Signal | Mode | Temperature | Note |
+| :-----: | :--------: | :---------: | ----------------------------------- |
+| Dh | Dehumidify | N/A | |
+| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
+| Cool_lo | Cooling | 23°C | |
+| Heat_hi | Heating | See note | Highest temperature in heating mode |
+| Heat_lo | Heating | 23°C | |
+
+Finally, record the `Off` signal:
+1. Make sure the display shows that A/C is ON.
+2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
+3. Press the **POWER** button so that the remote shows the OFF state.
+4. Save the resulting signal under the name `Off`.
+
+The resulting remote file should now contain 6 signals. Any of them can be omitted, but that will mean that this functionality will not be used.
+Test the file against the actual device. Every signal must do what it's supposed to.
+If everything checks out, add these signals to the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir)
+and open a pull request.
diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv
index 331cda812..8dff220ee 100644
--- a/firmware/targets/f7/api_symbols.csv
+++ b/firmware/targets/f7/api_symbols.csv
@@ -1,5 +1,5 @@
entry,status,name,type,params
-Version,+,1.6,,
+Version,+,1.7,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@@ -2580,8 +2580,14 @@ Variable,+,I_Circles_47x47,const Icon,
Variable,+,I_Clock_18x18,const Icon,
Variable,+,I_Connect_me_62x31,const Icon,
Variable,+,I_Connected_62x31,const Icon,
+Variable,+,I_CoolHi_25x27,const Icon,
+Variable,+,I_CoolHi_hvr_25x27,const Icon,
+Variable,+,I_CoolLo_25x27,const Icon,
+Variable,+,I_CoolLo_hvr_25x27,const Icon,
Variable,+,I_Cry_dolph_55x52,const Icon,
Variable,+,I_DFU_128x50,const Icon,
+Variable,+,I_Dehumidify_25x27,const Icon,
+Variable,+,I_Dehumidify_hvr_25x27,const Icon,
Variable,+,I_Detailed_chip_17x13,const Icon,
Variable,+,I_DolphinCommon_56x48,const Icon,
Variable,+,I_DolphinMafia_115x62,const Icon,
@@ -2605,6 +2611,10 @@ Variable,+,I_FaceNopower_29x14,const Icon,
Variable,+,I_FaceNormal_29x14,const Icon,
Variable,+,I_GameMode_11x8,const Icon,
Variable,+,I_Health_16x16,const Icon,
+Variable,+,I_HeatHi_25x27,const Icon,
+Variable,+,I_HeatHi_hvr_25x27,const Icon,
+Variable,+,I_HeatLo_25x27,const Icon,
+Variable,+,I_HeatLo_hvr_25x27,const Icon,
Variable,+,I_InfraredArrowDown_4x8,const Icon,
Variable,+,I_InfraredArrowUp_4x8,const Icon,
Variable,+,I_InfraredLearnShort_128x31,const Icon,
@@ -2622,6 +2632,8 @@ Variable,+,I_Mute_25x27,const Icon,
Variable,+,I_Mute_hvr_25x27,const Icon,
Variable,+,I_NFC_manual_60x50,const Icon,
Variable,+,I_Nfc_10px,const Icon,
+Variable,+,I_Off_25x27,const Icon,
+Variable,+,I_Off_hvr_25x27,const Icon,
Variable,+,I_Ok_btn_9x9,const Icon,
Variable,+,I_Ok_btn_pressed_13x13,const Icon,
Variable,+,I_Percent_10x14,const Icon,
From 3360f818a1d10771c2e70616ddedccca37d33a86 Mon Sep 17 00:00:00 2001
From: Max Lapan
Date: Tue, 20 Sep 2022 07:29:10 +0200
Subject: [PATCH 04/30] Subghz: Adding checks for get_upload functions (#1704)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Adding checks for get_upload functions
Almost in every protocol, function which generates upload might fail and return false.
But we don't check this result, which might end up sending random memory contents to the air.
* Format sources and fix crash on ivalid bit count in chamberlain
Co-authored-by: あく
---
lib/subghz/protocols/bett.c | 2 +-
lib/subghz/protocols/came.c | 2 +-
lib/subghz/protocols/chamberlain_code.c | 4 ++--
lib/subghz/protocols/clemsa.c | 2 +-
lib/subghz/protocols/doitrand.c | 2 +-
lib/subghz/protocols/gate_tx.c | 2 +-
lib/subghz/protocols/holtek.c | 2 +-
lib/subghz/protocols/honeywell_wdb.c | 2 +-
lib/subghz/protocols/hormann.c | 2 +-
lib/subghz/protocols/intertechno_v3.c | 2 +-
lib/subghz/protocols/keeloq.c | 2 +-
lib/subghz/protocols/linear.c | 2 +-
lib/subghz/protocols/magellen.c | 2 +-
lib/subghz/protocols/megacode.c | 2 +-
lib/subghz/protocols/nero_radio.c | 2 +-
lib/subghz/protocols/nero_sketch.c | 2 +-
lib/subghz/protocols/nice_flo.c | 2 +-
lib/subghz/protocols/phoenix_v2.c | 2 +-
lib/subghz/protocols/princeton.c | 2 +-
19 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c
index 08080dc6c..c80702577 100644
--- a/lib/subghz/protocols/bett.c
+++ b/lib/subghz/protocols/bett.c
@@ -173,7 +173,7 @@ bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flip
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_bett_get_upload(instance);
+ if(!subghz_protocol_encoder_bett_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c
index 14c66b7fa..53d3d0788 100644
--- a/lib/subghz/protocols/came.c
+++ b/lib/subghz/protocols/came.c
@@ -162,7 +162,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_came_get_upload(instance);
+ if(!subghz_protocol_encoder_came_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c
index 51f2bcd32..66d230d13 100644
--- a/lib/subghz/protocols/chamberlain_code.c
+++ b/lib/subghz/protocols/chamberlain_code.c
@@ -155,7 +155,7 @@ static bool
break;
default:
- furi_crash(TAG " unknown protocol.");
+ FURI_LOG_E(TAG, "Invalid bits count");
return false;
break;
}
@@ -224,7 +224,7 @@ bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_chamb_code_get_upload(instance);
+ if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c
index 357a0b06d..337346934 100644
--- a/lib/subghz/protocols/clemsa.c
+++ b/lib/subghz/protocols/clemsa.c
@@ -173,7 +173,7 @@ bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_clemsa_get_upload(instance);
+ if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c
index 9a0a58190..9122c1935 100644
--- a/lib/subghz/protocols/doitrand.c
+++ b/lib/subghz/protocols/doitrand.c
@@ -154,7 +154,7 @@ bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_doitrand_get_upload(instance);
+ if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c
index d7efb3862..56c224aef 100644
--- a/lib/subghz/protocols/gate_tx.c
+++ b/lib/subghz/protocols/gate_tx.c
@@ -147,7 +147,7 @@ bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* f
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_gate_tx_get_upload(instance);
+ if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c
index 137ba85d3..5cd160633 100644
--- a/lib/subghz/protocols/holtek.c
+++ b/lib/subghz/protocols/holtek.c
@@ -160,7 +160,7 @@ bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_holtek_get_upload(instance);
+ if(!subghz_protocol_encoder_holtek_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c
index e1e21426d..451a13f50 100644
--- a/lib/subghz/protocols/honeywell_wdb.c
+++ b/lib/subghz/protocols/honeywell_wdb.c
@@ -162,7 +162,7 @@ bool subghz_protocol_encoder_honeywell_wdb_deserialize(
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_honeywell_wdb_get_upload(instance);
+ if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c
index 0197f59e6..d78bc9273 100644
--- a/lib/subghz/protocols/hormann.c
+++ b/lib/subghz/protocols/hormann.c
@@ -163,7 +163,7 @@ bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* f
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_hormann_get_upload(instance);
+ if(!subghz_protocol_encoder_hormann_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c
index e70bb8c8b..ffe52e875 100644
--- a/lib/subghz/protocols/intertechno_v3.c
+++ b/lib/subghz/protocols/intertechno_v3.c
@@ -179,7 +179,7 @@ bool subghz_protocol_encoder_intertechno_v3_deserialize(
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_intertechno_v3_get_upload(instance);
+ if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c
index 0321a8767..99b3c5fb3 100644
--- a/lib/subghz/protocols/keeloq.c
+++ b/lib/subghz/protocols/keeloq.c
@@ -280,7 +280,7 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn);
+ if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break;
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c
index 92ba02a8f..8f7aed794 100644
--- a/lib/subghz/protocols/linear.c
+++ b/lib/subghz/protocols/linear.c
@@ -165,7 +165,7 @@ bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_linear_get_upload(instance);
+ if(!subghz_protocol_encoder_linear_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/magellen.c b/lib/subghz/protocols/magellen.c
index bb0600a74..52ef5a724 100644
--- a/lib/subghz/protocols/magellen.c
+++ b/lib/subghz/protocols/magellen.c
@@ -168,7 +168,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_magellen_get_upload(instance);
+ if(!subghz_protocol_encoder_magellen_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c
index 909e72171..1501580d8 100644
--- a/lib/subghz/protocols/megacode.c
+++ b/lib/subghz/protocols/megacode.c
@@ -193,7 +193,7 @@ bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_megacode_get_upload(instance);
+ if(!subghz_protocol_encoder_megacode_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c
index 69326f5a0..b5a7e8c0e 100644
--- a/lib/subghz/protocols/nero_radio.c
+++ b/lib/subghz/protocols/nero_radio.c
@@ -172,7 +172,7 @@ bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_nero_radio_get_upload(instance);
+ if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c
index c93b36a53..66ee569c2 100644
--- a/lib/subghz/protocols/nero_sketch.c
+++ b/lib/subghz/protocols/nero_sketch.c
@@ -166,7 +166,7 @@ bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperForma
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_nero_sketch_get_upload(instance);
+ if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c
index 07b18e3ea..f07e9efcc 100644
--- a/lib/subghz/protocols/nice_flo.c
+++ b/lib/subghz/protocols/nice_flo.c
@@ -149,7 +149,7 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_nice_flo_get_upload(instance);
+ if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c
index 3d2796e44..d680b2e62 100644
--- a/lib/subghz/protocols/phoenix_v2.c
+++ b/lib/subghz/protocols/phoenix_v2.c
@@ -150,7 +150,7 @@ bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_phoenix_v2_get_upload(instance);
+ if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c
index 2ddfa2cb6..a5b8134d8 100644
--- a/lib/subghz/protocols/princeton.c
+++ b/lib/subghz/protocols/princeton.c
@@ -167,7 +167,7 @@ bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
- subghz_protocol_encoder_princeton_get_upload(instance);
+ if(!subghz_protocol_encoder_princeton_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
From c213ff596a4df9bb0fa49abb42c4b1dfb8e241ac Mon Sep 17 00:00:00 2001
From: Matt Van Zanten
Date: Tue, 20 Sep 2022 11:45:16 -0700
Subject: [PATCH 05/30] adding support for HIDProx, updating the UI to switch
between protocols
---
applications/plugins/flipfrid/README.md | 28 +-
applications/plugins/flipfrid/flipfrid.c | 3 +
applications/plugins/flipfrid/flipfrid.h | 12 +-
.../scene/flipfrid_scene_entrypoint.c | 76 ++++-
.../flipfrid/scene/flipfrid_scene_load_file.c | 43 ++-
.../scene/flipfrid_scene_run_attack.c | 285 +++++++++++++-----
6 files changed, 348 insertions(+), 99 deletions(-)
diff --git a/applications/plugins/flipfrid/README.md b/applications/plugins/flipfrid/README.md
index 51ed2fa67..69fdb3e66 100644
--- a/applications/plugins/flipfrid/README.md
+++ b/applications/plugins/flipfrid/README.md
@@ -1,21 +1,35 @@
# Flipfrid
-Basic EM4100 Fuzzer
+Basic EM4100 and HIDProx Fuzzer.
## Why
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz).
Objective is to provide a simple to use fuzzer to test readers by emulating various cards.
-EM4100 cards use a 1 byte customer id and 4 bytes card id.
+- EM4100 cards use a 1 byte customer id and 4 bytes card id.
+- HIDProx cards use a 2 byte customer id and 3 byte card id.
## How
-There is 4 modes :
-- Default key loop over 16 factory/default keys and emulate each one after one ;
-- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ;
-- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
-- Uids list: loop over a text file (one uid per line)
+1) Select the Protocol with the left and right arrows
+2) Select the Mode with the up and down arrows
+
+### Info
+
+There are 2 Protocols:
+- EM4100
+- HIDProx
+
+There are 4 modes:
+- Default Values: Try factory/default keys and emulate one after the other.
+- BF customer id: An iteration from 0X00 to 0XFF on the first byte.
+- Load Dump file: Load an existing dump (.rfid) generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
+- Uids list: Iterate over an input text file (one uid per line) and emulate one after the other.
+
+
+
TODO :
- blank screen on back press
+- Add second byte test to `BF customer id`
diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/plugins/flipfrid/flipfrid.c
index 1dab3cae1..ba059ff7f 100644
--- a/applications/plugins/flipfrid/flipfrid.c
+++ b/applications/plugins/flipfrid/flipfrid.c
@@ -64,6 +64,7 @@ FlipFridState* flipfrid_alloc() {
flipfrid->is_attacking = false;
flipfrid->key_index = 0;
flipfrid->menu_index = 0;
+ flipfrid->menu_proto_index = 0;
flipfrid->attack = FlipFridAttackDefaultValues;
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
@@ -73,12 +74,14 @@ FlipFridState* flipfrid_alloc() {
flipfrid->data[2] = 0x00;
flipfrid->data[3] = 0x00;
flipfrid->data[4] = 0x00;
+ flipfrid->data[5] = 0x00;
flipfrid->payload[0] = 0x00;
flipfrid->payload[1] = 0x00;
flipfrid->payload[2] = 0x00;
flipfrid->payload[3] = 0x00;
flipfrid->payload[4] = 0x00;
+ flipfrid->payload[5] = 0x00;
//Dialog
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);
diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/plugins/flipfrid/flipfrid.h
index 44756f26e..5417817e9 100644
--- a/applications/plugins/flipfrid/flipfrid.h
+++ b/applications/plugins/flipfrid/flipfrid.h
@@ -28,6 +28,11 @@ typedef enum {
FlipFridAttackLoadFileCustomUids,
} FlipFridAttacks;
+typedef enum {
+ EM4100,
+ HIDProx,
+} FlipFridProtos;
+
typedef enum {
NoneScene,
SceneEntryPoint,
@@ -56,13 +61,16 @@ typedef struct {
FlipFridScene previous_scene;
NotificationApp* notify;
u_int8_t menu_index;
+ u_int8_t menu_proto_index;
string_t data_str;
- uint8_t data[5];
- uint8_t payload[5];
+ uint8_t data[6];
+ uint8_t payload[6];
uint8_t attack_step;
FlipFridAttacks attack;
+ FlipFridProtos proto;
string_t attack_name;
+ string_t proto_name;
DialogsApp* dialogs;
string_t notification_msg;
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
index f30bb8e1d..c71a35903 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
@@ -1,8 +1,9 @@
#include "flipfrid_scene_entrypoint.h"
string_t menu_items[4];
+string_t menu_proto_items[2];
-void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) {
+void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index, uint32_t proto_index) {
switch(index) {
case FlipFridAttackDefaultValues:
context->attack = FlipFridAttackDefaultValues;
@@ -27,6 +28,19 @@ void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t in
default:
break;
}
+
+ switch(proto_index) {
+ case EM4100:
+ context->proto = EM4100;
+ string_set_str(context->proto_name, "EM4100");
+ break;
+ case HIDProx:
+ context->proto = HIDProx;
+ string_set_str(context->proto_name, "HIDProx");
+ break;
+ default:
+ break;
+ }
}
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
@@ -36,6 +50,7 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 0x00;
+ context->payload[5] = 0x00;
context->menu_index = 0;
for(uint32_t i = 0; i < 4; i++) {
@@ -46,6 +61,14 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
string_set(menu_items[1], "BF Customer ID");
string_set(menu_items[2], "Load File");
string_set(menu_items[3], "Load uids from file");
+
+ context->menu_proto_index = 0;
+ for(uint32_t i = 0; i < 2; i++) {
+ string_init(menu_proto_items[i]);
+ }
+
+ string_set(menu_proto_items[0], "EM4100");
+ string_set(menu_proto_items[1], "HIDProx");
}
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
@@ -53,6 +76,10 @@ void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
for(uint32_t i = 0; i < 4; i++) {
string_clear(menu_items[i]);
}
+
+ for(uint32_t i = 0; i < 2; i++) {
+ string_clear(menu_proto_items[i]);
+ }
}
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
@@ -74,10 +101,17 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont
}
break;
case InputKeyLeft:
+ if(context->menu_proto_index > EM4100) {
+ context->menu_proto_index--;
+ }
+ break;
case InputKeyRight:
+ if(context->menu_proto_index < HIDProx) {
+ context->menu_proto_index++;
+ }
break;
case InputKeyOk:
- flipfrid_scene_entrypoint_menu_callback(context, context->menu_index);
+ flipfrid_scene_entrypoint_menu_callback(context, context->menu_index, context->menu_proto_index);
break;
case InputKeyBack:
context->is_running = false;
@@ -91,10 +125,6 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
- // Title
- canvas_set_font(canvas, FontPrimary);
- canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer");
-
if(context->menu_index > FlipFridAttackDefaultValues) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
@@ -120,4 +150,38 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
AlignTop,
string_get_cstr(menu_items[context->menu_index + 1]));
}
+
+ if(context->menu_proto_index > EM4100) {
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(
+ canvas,
+ 64,
+ -12,
+ AlignCenter,
+ AlignTop,
+ string_get_cstr(menu_proto_items[context->menu_proto_index - 1]));
+ }
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(
+ canvas, 34, 4, AlignCenter, AlignTop, "<");
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(
+ canvas, 64, 4, AlignCenter, AlignTop, string_get_cstr(menu_proto_items[context->menu_proto_index]));
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(
+ canvas, 94, 4, AlignCenter, AlignTop, ">");
+
+ if(context->menu_proto_index < HIDProx) {
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(
+ canvas,
+ 64,
+ -12,
+ AlignCenter,
+ AlignTop,
+ string_get_cstr(menu_proto_items[context->menu_proto_index + 1]));
+ }
}
\ No newline at end of file
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
index 072185ccf..ded62750a 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
@@ -36,11 +36,21 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
break;
} else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
- if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
- FURI_LOG_E(TAG, "Unsupported Key type");
- string_reset(context->notification_msg);
- string_set_str(context->notification_msg, "Unsupported Key type");
- break;
+
+ if(context->proto == EM4100) {
+ if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
+ FURI_LOG_E(TAG, "Unsupported Key type");
+ string_reset(context->notification_msg);
+ string_set_str(context->notification_msg, "Unsupported Key type");
+ break;
+ }
+ } else {
+ if(strcmp(string_get_cstr(temp_str), "HIDProx") != 0) {
+ FURI_LOG_E(TAG, "Unsupported Key type");
+ string_reset(context->notification_msg);
+ string_set_str(context->notification_msg, "Unsupported Key type");
+ break;
+ }
}
}
@@ -53,15 +63,24 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
} else {
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
- // Check data size
- if(string_size(context->data_str) != 14) {
- FURI_LOG_E(TAG, "Incorrect Key length");
- string_reset(context->notification_msg);
- string_set_str(context->notification_msg, "Incorrect Key length");
- break;
+ if(context->proto == EM4100) {
+ if(string_size(context->data_str) != 14) {
+ FURI_LOG_E(TAG, "Incorrect Key length");
+ string_reset(context->notification_msg);
+ string_set_str(context->notification_msg, "Incorrect Key length");
+ break;
+ }
+ } else {
+ if(string_size(context->data_str) != 17) {
+ FURI_LOG_E(TAG, "Incorrect Key length");
+ string_reset(context->notification_msg);
+ string_set_str(context->notification_msg, "Incorrect Key length");
+ break;
+ }
}
+
// String to uint8_t
- for(uint8_t i = 0; i < 5; i++) {
+ for(uint8_t i = 0; i < 6; i++) {
char temp_str2[3];
temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
index e34cb8986..707ac74f0 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
@@ -3,7 +3,7 @@
uint8_t counter = 0;
#define TIME_BETWEEN_CARDS 5
-uint8_t id_list[16][5] = {
+uint8_t id_list[17][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
@@ -16,17 +16,34 @@ uint8_t id_list[16][5] = {
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
+ {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
};
+uint8_t id_list_hid[14][6] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
+ {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
+ {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
+ {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
+ {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
+ {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
+ {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
+ {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
+ {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
+ {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
+ {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID
+ {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID
+ {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
+};
+
void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
context->attack_step = 0;
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
context->worker = lfrfid_worker_alloc(context->dict);
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
}
void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
@@ -40,7 +57,7 @@ void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
if(context->is_attacking) {
if(1 == counter) {
- protocol_dict_set_data(context->dict, context->protocol, context->payload, 5);
+ protocol_dict_set_data(context->dict, context->protocol, context->payload, 6);
lfrfid_worker_free(context->worker);
context->worker = lfrfid_worker_alloc(context->dict);
lfrfid_worker_start_thread(context->worker);
@@ -50,88 +67,198 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
lfrfid_worker_stop_thread(context->worker);
switch(context->attack) {
case FlipFridAttackDefaultValues:
- context->payload[0] = id_list[context->attack_step][0];
- context->payload[1] = id_list[context->attack_step][1];
- context->payload[2] = id_list[context->attack_step][2];
- context->payload[3] = id_list[context->attack_step][3];
- context->payload[4] = id_list[context->attack_step][4];
+ if(context->proto == EM4100) {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
- if(context->attack_step == 15) {
- context->attack_step = 0;
- counter = 0;
- context->is_attacking = false;
- notification_message(context->notify, &sequence_blink_stop);
- notification_message(context->notify, &sequence_single_vibro);
+ context->payload[0] = id_list[context->attack_step][0];
+ context->payload[1] = id_list[context->attack_step][1];
+ context->payload[2] = id_list[context->attack_step][2];
+ context->payload[3] = id_list[context->attack_step][3];
+ context->payload[4] = id_list[context->attack_step][4];
- } else {
- context->attack_step++;
- }
- break;
-
- case FlipFridAttackBfCustomerId:
- context->payload[0] = context->attack_step;
- context->payload[1] = 0x00;
- context->payload[2] = 0x00;
- context->payload[3] = 0x00;
- context->payload[4] = 0x00;
-
- if(context->attack_step == 255) {
- context->attack_step = 0;
- counter = 0;
- context->is_attacking = false;
- notification_message(context->notify, &sequence_blink_stop);
- notification_message(context->notify, &sequence_single_vibro);
- } else {
- context->attack_step++;
- }
- break;
- case FlipFridAttackLoadFile:
- context->payload[0] = context->data[0];
- context->payload[1] = context->data[1];
- context->payload[2] = context->data[2];
- context->payload[3] = context->data[3];
- context->payload[4] = context->data[4];
-
- context->payload[context->key_index] = context->attack_step;
-
- if(context->attack_step == 255) {
- context->attack_step = 0;
- counter = 0;
- context->is_attacking = false;
- notification_message(context->notify, &sequence_blink_stop);
- notification_message(context->notify, &sequence_single_vibro);
+ if(context->attack_step == 15) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ } else {
+ context->attack_step++;
+ }
break;
} else {
- context->attack_step++;
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+
+ context->payload[0] = id_list_hid[context->attack_step][0];
+ context->payload[1] = id_list_hid[context->attack_step][1];
+ context->payload[2] = id_list_hid[context->attack_step][2];
+ context->payload[3] = id_list_hid[context->attack_step][3];
+ context->payload[4] = id_list_hid[context->attack_step][4];
+ context->payload[5] = id_list_hid[context->attack_step][5];
+
+ if(context->attack_step == 15) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+
+ } else {
+ context->attack_step++;
+ }
+ break;
}
- break;
- case FlipFridAttackLoadFileCustomUids:
- while(true) {
- string_reset(context->data_str);
- if(!stream_read_line(context->uids_stream, context->data_str)) {
+
+ case FlipFridAttackBfCustomerId:
+ if(context->proto == EM4100) {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+
+ context->payload[0] = context->attack_step;
+ context->payload[1] = 0x00;
+ context->payload[2] = 0x00;
+ context->payload[3] = 0x00;
+ context->payload[4] = 0x00;
+
+ if(context->attack_step == 255) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ } else {
+ context->attack_step++;
+ }
+ break;
+ } else {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+
+ context->payload[0] = context->attack_step;
+ context->payload[1] = 0x00;
+ context->payload[2] = 0x00;
+ context->payload[3] = 0x00;
+ context->payload[4] = 0x00;
+ context->payload[5] = 0x00;
+
+ if(context->attack_step == 255) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ } else {
+ context->attack_step++;
+ }
+ break;
+ }
+
+ case FlipFridAttackLoadFile:
+ if(context->proto == EM4100) {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+
+ context->payload[0] = context->data[0];
+ context->payload[1] = context->data[1];
+ context->payload[2] = context->data[2];
+ context->payload[3] = context->data[3];
+ context->payload[4] = context->data[4];
+
+ context->payload[context->key_index] = context->attack_step;
+
+ if(context->attack_step == 255) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
break;
- };
- if(string_get_char(context->data_str, 0) == '#') continue;
- if(string_size(context->data_str) != 11) continue;
+ } else {
+ context->attack_step++;
+ }
+ break;
+ } else {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+
+ context->payload[0] = context->data[0];
+ context->payload[1] = context->data[1];
+ context->payload[2] = context->data[2];
+ context->payload[3] = context->data[3];
+ context->payload[4] = context->data[4];
+ context->payload[5] = context->data[5];
+
+ context->payload[context->key_index] = context->attack_step;
+
+ if(context->attack_step == 255) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ break;
+ } else {
+ context->attack_step++;
+ }
break;
}
- FURI_LOG_D(TAG, string_get_cstr(context->data_str));
- // string is valid, parse it in context->payload
- for(uint8_t i = 0; i < 5; i++) {
- char temp_str[3];
- temp_str[0] = string_get_cstr(context->data_str)[i * 2];
- temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
- temp_str[2] = '\0';
- context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
+ case FlipFridAttackLoadFileCustomUids:
+ if(context->proto == EM4100) {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+
+ while(true) {
+ string_reset(context->data_str);
+ if(!stream_read_line(context->uids_stream, context->data_str)) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ break;
+ };
+ if(string_get_char(context->data_str, 0) == '#') continue;
+ if(string_size(context->data_str) != 11) continue;
+ break;
+ }
+ FURI_LOG_D(TAG, string_get_cstr(context->data_str));
+
+ // string is valid, parse it in context->payload
+ for(uint8_t i = 0; i < 5; i++) {
+ char temp_str[3];
+ temp_str[0] = string_get_cstr(context->data_str)[i * 2];
+ temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
+ temp_str[2] = '\0';
+ context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
+ }
+ break;
+ } else {
+ context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+
+ while(true) {
+ string_reset(context->data_str);
+ if(!stream_read_line(context->uids_stream, context->data_str)) {
+ context->attack_step = 0;
+ counter = 0;
+ context->is_attacking = false;
+ notification_message(context->notify, &sequence_blink_stop);
+ notification_message(context->notify, &sequence_single_vibro);
+ break;
+ };
+ if(string_get_char(context->data_str, 0) == '#') continue;
+ if(string_size(context->data_str) != 13) continue;
+ break;
+ }
+ FURI_LOG_D(TAG, string_get_cstr(context->data_str));
+
+ // string is valid, parse it in context->payload
+ for(uint8_t i = 0; i < 6; i++) {
+ char temp_str[3];
+ temp_str[0] = string_get_cstr(context->data_str)[i * 2];
+ temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
+ temp_str[2] = '\0';
+ context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
+ }
+ break;
}
- break;
}
+
}
if(counter > TIME_BETWEEN_CARDS) {
@@ -189,9 +316,21 @@ void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
-
- char uid[16];
- snprintf(
+
+ char uid[18];
+ if(context->protocol == protocol_dict_get_protocol_by_name(context->dict, "HIDProx")) {
+ snprintf(
+ uid,
+ sizeof(uid),
+ "%02X:%02X:%02X:%02X:%02X:%02X",
+ context->payload[0],
+ context->payload[1],
+ context->payload[2],
+ context->payload[3],
+ context->payload[4],
+ context->payload[5]);
+ } else {
+ snprintf(
uid,
sizeof(uid),
"%02X:%02X:%02X:%02X:%02X",
@@ -200,6 +339,8 @@ void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
context->payload[2],
context->payload[3],
context->payload[4]);
+ }
+
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary);
From 96ad7f3cef86d0d853b01c40311a7757c93b57c8 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Tue, 20 Sep 2022 23:13:15 +0300
Subject: [PATCH 06/30] fix nfc list crash, fix magellen gui, fix transmitter
gui
---
.../main/nfc/scenes/nfc_scene_mf_classic_keys_list.c | 5 ++++-
applications/main/subghz/views/transmitter.c | 7 +++++--
lib/subghz/protocols/magellen.c | 2 +-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
index 36f01897e..bc6e5cb57 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
@@ -17,10 +17,13 @@ void nfc_scene_mf_classic_keys_list_on_enter(void* context) {
if(dict) {
mf_classic_dict_rewind(dict);
while(mf_classic_dict_get_next_key_str(dict, temp_key)) {
+ if(index > 200) {
+ break;
+ }
char* current_key = (char*)malloc(sizeof(char) * 13);
strncpy(current_key, string_get_cstr(temp_key), 12);
MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key);
- FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key);
+ FURI_LOG_T("ListKeys", "Key %d: %s", index, current_key);
submenu_add_item(
submenu,
current_key,
diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c
index be9c0fe09..2055180a6 100644
--- a/applications/main/subghz/views/transmitter.c
+++ b/applications/main/subghz/views/transmitter.c
@@ -45,7 +45,7 @@ void subghz_view_transmitter_add_data_to_show(
}
static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) {
- const uint8_t button_height = 13;
+ const uint8_t button_height = 12;
const uint8_t vertical_offset = 3;
const uint8_t horizontal_offset = 1;
const uint8_t string_width = canvas_string_width(canvas, str);
@@ -69,7 +69,10 @@ static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str
canvas_invert_color(canvas);
canvas_draw_icon(
- canvas, x + horizontal_offset, y - button_height + vertical_offset, &I_ButtonCenter_7x7);
+ canvas,
+ x + horizontal_offset,
+ y - button_height + vertical_offset - 1,
+ &I_ButtonCenter_7x7);
canvas_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas);
diff --git a/lib/subghz/protocols/magellen.c b/lib/subghz/protocols/magellen.c
index 52ef5a724..6dcc83e56 100644
--- a/lib/subghz/protocols/magellen.c
+++ b/lib/subghz/protocols/magellen.c
@@ -381,7 +381,7 @@ static void subghz_protocol_magellen_get_event_serialize(uint8_t event, string_t
"%s%s%s%s%s%s%s%s",
((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") :
(event & 0x1 ? " Motion" : " Ok")),
- ((event >> 1) & 0x1 ? ", Tamper On (Alarm)" : ""),
+ ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""),
((event >> 2) & 0x1 ? ", ?" : ""),
((event >> 3) & 0x1 ? ", Power On" : ""),
((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""),
From ca02826cfd669f31261448f8d3384cd505976990 Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Tue, 20 Sep 2022 23:24:34 +0300
Subject: [PATCH 07/30] set time between cards to 6, run fbt format
---
.../main/archive/helpers/archive_menu.h | 5 +-
.../archive/scenes/archive_scene_browser.c | 2 +-
.../scene/flipfrid_scene_entrypoint.c | 21 ++++--
.../flipfrid/scene/flipfrid_scene_load_file.c | 2 +-
.../scene/flipfrid_scene_run_attack.c | 69 ++++++++++---------
.../notification_settings_app.c | 15 +---
lib/nfc/helpers/reader_analyzer.c | 6 +-
7 files changed, 61 insertions(+), 59 deletions(-)
diff --git a/applications/main/archive/helpers/archive_menu.h b/applications/main/archive/helpers/archive_menu.h
index 5df6a1ca2..201333987 100644
--- a/applications/main/archive/helpers/archive_menu.h
+++ b/applications/main/archive/helpers/archive_menu.h
@@ -42,10 +42,7 @@ ARRAY_DEF(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
// Using in applications/archive/views/archive_browser_view.c
-static void archive_menu_add_item(
- ArchiveContextMenuItem_t* obj,
- string_t text,
- uint32_t event) {
+static void archive_menu_add_item(ArchiveContextMenuItem_t* obj, string_t text, uint32_t event) {
string_init_move(obj->text, text);
obj->event = event;
}
diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c
index 23722e30b..552a6557e 100644
--- a/applications/main/archive/scenes/archive_scene_browser.c
+++ b/applications/main/archive/scenes/archive_scene_browser.c
@@ -133,7 +133,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
case ArchiveBrowserEventFileMenuRename:
if(favorites) {
browser->callback(ArchiveBrowserEventEnterFavMove, browser->context);
- //} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) {
+ //} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) {
} else {
// Added ability to rename files and folders
archive_show_file_menu(browser, false);
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
index c71a35903..c709572e3 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
@@ -3,7 +3,10 @@
string_t menu_items[4];
string_t menu_proto_items[2];
-void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index, uint32_t proto_index) {
+void flipfrid_scene_entrypoint_menu_callback(
+ FlipFridState* context,
+ uint32_t index,
+ uint32_t proto_index) {
switch(index) {
case FlipFridAttackDefaultValues:
context->attack = FlipFridAttackDefaultValues;
@@ -111,7 +114,8 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont
}
break;
case InputKeyOk:
- flipfrid_scene_entrypoint_menu_callback(context, context->menu_index, context->menu_proto_index);
+ flipfrid_scene_entrypoint_menu_callback(
+ context, context->menu_index, context->menu_proto_index);
break;
case InputKeyBack:
context->is_running = false;
@@ -163,16 +167,19 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
}
canvas_set_font(canvas, FontPrimary);
- canvas_draw_str_aligned(
- canvas, 34, 4, AlignCenter, AlignTop, "<");
+ canvas_draw_str_aligned(canvas, 34, 4, AlignCenter, AlignTop, "<");
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
- canvas, 64, 4, AlignCenter, AlignTop, string_get_cstr(menu_proto_items[context->menu_proto_index]));
+ canvas,
+ 64,
+ 4,
+ AlignCenter,
+ AlignTop,
+ string_get_cstr(menu_proto_items[context->menu_proto_index]));
canvas_set_font(canvas, FontPrimary);
- canvas_draw_str_aligned(
- canvas, 94, 4, AlignCenter, AlignTop, ">");
+ canvas_draw_str_aligned(canvas, 94, 4, AlignCenter, AlignTop, ">");
if(context->menu_proto_index < HIDProx) {
canvas_set_font(canvas, FontSecondary);
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
index ded62750a..687a861c9 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
@@ -36,7 +36,7 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
break;
} else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
-
+
if(context->proto == EM4100) {
if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
FURI_LOG_E(TAG, "Unsupported Key type");
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
index 707ac74f0..f7776fb4d 100644
--- a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
+++ b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
@@ -2,7 +2,7 @@
#include
uint8_t counter = 0;
-#define TIME_BETWEEN_CARDS 5
+#define TIME_BETWEEN_CARDS 6
uint8_t id_list[17][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
@@ -68,7 +68,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
switch(context->attack) {
case FlipFridAttackDefaultValues:
if(context->proto == EM4100) {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "EM4100");
context->payload[0] = id_list[context->attack_step][0];
context->payload[1] = id_list[context->attack_step][1];
@@ -87,7 +88,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
}
break;
} else {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
context->payload[0] = id_list_hid[context->attack_step][0];
context->payload[1] = id_list_hid[context->attack_step][1];
@@ -95,7 +97,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
context->payload[3] = id_list_hid[context->attack_step][3];
context->payload[4] = id_list_hid[context->attack_step][4];
context->payload[5] = id_list_hid[context->attack_step][5];
-
+
if(context->attack_step == 15) {
context->attack_step = 0;
counter = 0;
@@ -111,7 +113,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
case FlipFridAttackBfCustomerId:
if(context->proto == EM4100) {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "EM4100");
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
@@ -130,7 +133,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
}
break;
} else {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
@@ -150,10 +154,11 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
}
break;
}
-
+
case FlipFridAttackLoadFile:
if(context->proto == EM4100) {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "EM4100");
context->payload[0] = context->data[0];
context->payload[1] = context->data[1];
@@ -175,7 +180,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
}
break;
} else {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
context->payload[0] = context->data[0];
context->payload[1] = context->data[1];
@@ -201,7 +207,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
case FlipFridAttackLoadFileCustomUids:
if(context->proto == EM4100) {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "EM4100");
while(true) {
string_reset(context->data_str);
@@ -229,7 +236,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
}
break;
} else {
- context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
+ context->protocol =
+ protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
while(true) {
string_reset(context->data_str);
@@ -258,7 +266,6 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
break;
}
}
-
}
if(counter > TIME_BETWEEN_CARDS) {
@@ -316,31 +323,31 @@ void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
-
+
char uid[18];
if(context->protocol == protocol_dict_get_protocol_by_name(context->dict, "HIDProx")) {
snprintf(
- uid,
- sizeof(uid),
- "%02X:%02X:%02X:%02X:%02X:%02X",
- context->payload[0],
- context->payload[1],
- context->payload[2],
- context->payload[3],
- context->payload[4],
- context->payload[5]);
+ uid,
+ sizeof(uid),
+ "%02X:%02X:%02X:%02X:%02X:%02X",
+ context->payload[0],
+ context->payload[1],
+ context->payload[2],
+ context->payload[3],
+ context->payload[4],
+ context->payload[5]);
} else {
snprintf(
- uid,
- sizeof(uid),
- "%02X:%02X:%02X:%02X:%02X",
- context->payload[0],
- context->payload[1],
- context->payload[2],
- context->payload[3],
- context->payload[4]);
+ uid,
+ sizeof(uid),
+ "%02X:%02X:%02X:%02X:%02X",
+ context->payload[0],
+ context->payload[1],
+ context->payload[2],
+ context->payload[3],
+ context->payload[4]);
}
-
+
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary);
diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c
index bfda689ea..db9a1a01f 100644
--- a/applications/settings/notification_settings/notification_settings_app.c
+++ b/applications/settings/notification_settings/notification_settings_app.c
@@ -60,19 +60,8 @@ const char* const delay_text[DELAY_COUNT] = {
"10min",
"30min",
};
-const uint32_t delay_value[DELAY_COUNT] = {
- 1000,
- 5000,
- 10000,
- 15000,
- 30000,
- 60000,
- 90000,
- 120000,
- 300000,
- 600000,
- 1800000
-};
+const uint32_t delay_value[DELAY_COUNT] =
+ {1000, 5000, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000, 1800000};
#define VIBRO_COUNT 2
const char* const vibro_text[VIBRO_COUNT] = {
diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c
index 90b917296..3d065d144 100644
--- a/lib/nfc/helpers/reader_analyzer.c
+++ b/lib/nfc/helpers/reader_analyzer.c
@@ -39,7 +39,8 @@ struct ReaderAnalyzer {
NfcDebugPcap* pcap;
};
-static FuriHalNfcDevData reader_analyzer_nfc_data[] = { //XXX
+static FuriHalNfcDevData reader_analyzer_nfc_data[] = {
+ //XXX
[ReaderAnalyzerNfcDataMfClassic] =
{.sak = 0x08,
.atqa = {0x44, 0x00},
@@ -101,7 +102,8 @@ int32_t reader_analyzer_thread(void* context) {
ReaderAnalyzer* reader_analyzer_alloc() {
ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer));
reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic].cuid = rand(); //XXX
- furi_hal_random_fill_buf((uint8_t*) &reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic].uid, 7);
+ furi_hal_random_fill_buf(
+ (uint8_t*)&reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic].uid, 7);
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
instance->alive = false;
instance->stream =
From daee9db3667dd2bf3258efd985d9494b628255e3 Mon Sep 17 00:00:00 2001
From: RogueMaster
Date: Tue, 20 Sep 2022 17:43:59 -0400
Subject: [PATCH 08/30] Clock in menu from fap
---
.../desktop/scenes/desktop_scene_main.c | 20 +++---------------
applications/services/loader/loader.c | 21 ++++++++++++++++++-
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c
index f50481906..68f712891 100644
--- a/applications/services/desktop/scenes/desktop_scene_main.c
+++ b/applications/services/desktop/scenes/desktop_scene_main.c
@@ -86,11 +86,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
consumed = true;
break;
- // case DesktopMainEventOpenGames:
- // loader_show_game_menu();
- // consumed = true;
- // break;
-
case DesktopMainEventOpenLockMenu:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu);
consumed = true;
@@ -117,12 +112,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
break;
}
case DesktopMainEventOpenClock: {
- // loader_start(desktop->loader, FLIPPER_APPS[0].name, NULL);
- LoaderStatus status = loader_start(
- desktop->loader, "Applications", EXT_PATH("/apps/Main/Clock.fap"));
- if(status != LoaderStatusOk) {
- FURI_LOG_E(TAG, "loader_start failed: %d", status);
- }
+ // it has its own error
+ LoaderStatus status = loader_start(desktop->loader, "Applications", EXT_PATH("/apps/Main/Clock.fap"));
consumed = true;
break;
}
@@ -233,12 +224,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
break;
}
case DesktopMainEventOpenSubRemote: {
- loader_start(desktop->loader, FLIPPER_APPS[1].name, NULL);
- // LoaderStatus status = loader_start(
- // desktop->loader, "Applications", EXT_PATH("/apps/Main/SubGHz_Remote.fap"));
- // if(status != LoaderStatusOk) {
- // FURI_LOG_E(TAG, "loader_start failed: %d", status);
- // }
+ loader_start(desktop->loader, FLIPPER_APPS[2].name, NULL);
consumed = true;
break;
}
diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c
index 649b01476..fbf5cb937 100644
--- a/applications/services/loader/loader.c
+++ b/applications/services/loader/loader.c
@@ -1,7 +1,9 @@
#include "applications.h"
#include
+#include
#include "loader/loader.h"
#include "loader_i.h"
+#include "applications/services/desktop/desktop_i.h"
#define TAG "LoaderSrv"
@@ -55,6 +57,16 @@ static void loader_submenu_callback(void* context, uint32_t index) {
view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id);
}
+static void loader_clock_callback(void* context, uint32_t index) {
+ UNUSED(index);
+ Desktop* desktop = desktop_alloc();
+ LoaderStatus status = loader_start(
+ desktop->loader, "Applications", EXT_PATH("/apps/Main/Clock.fap"));
+ if(status != LoaderStatusOk) {
+ FURI_LOG_E(TAG, "loader_start failed: %d", status);
+ }
+}
+
static void loader_cli_print_usage() {
printf("Usage:\r\n");
printf("loader