From efad1709b80eaf0d98b6296efc65081d91c00d53 Mon Sep 17 00:00:00 2001 From: Zinong Li <131403964+zinongli@users.noreply.github.com> Date: Tue, 4 Feb 2025 03:47:05 -0500 Subject: [PATCH 01/16] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 17468a1..1347610 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Key Copier App A Flipper Zero app for measuring key bitting patterns. +## Installation +The best way to install the latest build would be download it from Flipper Zero's Official App Store: +https://lab.flipper.net/apps/key_copier + ## Instruction To measure your key: 1. Place it on top of the screen. From e4c92ed6e297e2f6e30b2919f9d66933265dd356 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:03:02 -0500 Subject: [PATCH 02/16] bug fix --- key_copier.c | 1 - 1 file changed, 1 deletion(-) diff --git a/key_copier.c b/key_copier.c index d16e199..61ef3c9 100644 --- a/key_copier.c +++ b/key_copier.c @@ -701,7 +701,6 @@ static KeyCopierApp* key_copier_app_alloc() { app->view_dispatcher, KeyCopierViewTextInput, text_input_get_view(app->text_input)); app->temp_buffer_size = 32; app->temp_buffer = (char*)malloc(app->temp_buffer_size); - app->temp_buffer = ""; app->view_measure = view_alloc(); view_set_draw_callback(app->view_measure, key_copier_view_measure_draw_callback); From 23959760b91d1a7d6bea50c20ce3c2d910d361f7 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:06:15 -0500 Subject: [PATCH 03/16] update screenshots --- screenshots/config.png | Bin 1301 -> 1527 bytes screenshots/main_menu.png | Bin 1528 -> 1765 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/config.png b/screenshots/config.png index aae6eabfc9cc59f3718bff2953b0de7a59b6131e..fae7df6ac5980d7aedc99c379de3695d80b72321 100644 GIT binary patch literal 1527 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z%2~Ij105pNH8$4 zuJm+q45^s&_U_r-+g=h57c~kjSL(Yi*>^>FkE@vy&%O!I?oaqWCzio#Q3|KH@fG*| z|Ni{?$lH+q^WW$1`}-{)+Av5=U|?|JWN^50{I~tIH92hBTl;kRU#)_RsGLsrTNyZ+s#rlHI@m)U@Au{eSkj6-@u`a5irKQ`P_H zXCD7BQxtB{mAvZ=({}u}yDNKMvd`d9dgc24-PKzJ<5pXSF)#!&F$kyvsrGp08=L-b z{gxkl@9FPD(etj)+4(*`-?Hv)`GM^=*Esfz|GS^DfAQA$KUc4r+iNiy$#li*4Bu{j zm(P{2e_Qwc&iOpcw6~vaAJts@_vX&eo^A4{((BxROTGP@&kC^!XkY0G(}um@cDnt} zTW=eX<#v1XzTFq&oV6JmG#D8=gcurD9DdKbC6&Lh#n$M7{&nVkX-}@7;$l$nVqjRr z!l3Z#%6`s0Dg1?}YhStVv;PtI{l5Ic6YGtiTRhra^ZoqMITO#{M^aq+m2*z+llM=U z)@*%xs}srDD~lP@t6r6NF@8%zxa{kvy%i1BkwyG^z=UuH=oFBPW}RW&vEACC9if>} z4B?ACgxr&i~s{duD6@*3|mTTN(Slt@!(5?mEvmU+eGv{pac(fk>x2 z#Rcx%xchzO^?9GCJ%2sxPCAl7hwpN8{Fr4O2ej>6c755C>kqY%WS#GGXLN4;{{8;x z(0*=jotyIUpXB1I&xm=St9)xYr}AHU3Q`Pcy{~7qxjHF(GLiw%G)e^ZPmbZh;j{mG zpKANDHN1v~^-eYhfm9?b3cqqP6zBkB*oOm>NoF!LbZlY*r#(=F8?iHdD0s%eFhc;6 z0DydC{i%Em4<4RjWH3;KWFH`30V#c+k^wpaXkvmBB)tOp4pWfQjwM4wyD>Y%ffi^s z2l6!#F{}Y}7JoV)LjxxyFM#-gNI?N~mfSfR1_ohhU;z1x)<7%*uoe7TOF9p00i_>zopr0A3FG5&!@I literal 1301 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z%2~Ij105pNH8$4 z9QSl_45^s&_O4^mEdvIJi}SExpr*pW5Xi(J@b%-b!?P@v8P=I*y}ic4py0*8u!x00;Z@;R zPKFPq-me+$*clEeF*0-rF*Io4p;q*t=Z}0|YTspA9gt)DPpkUd-FZLj&OXpP&-gF@ zY<9})>O=ExM;8B{m+$Weu~`*J9lpkJZO3n!JF@3zE?XR*z4+RnzBp@{uXlI8KZWF$ z52g*a#S^3Vzb-m^e^#Pw()qtpn@ra4dj3Os-Q75=y0-tP@1Ff5(PuS3@HMZEBqM_h zCxgQj1_l?|O>72P_v*J*{;{o|+OsP@`u%&s=bOKlPd}Ib%<}c_9j^@UY<^b4a(?N0 zLngQftx|XsuGqZKx&C%mbc&hrnw4k1{guzYXTC-f#pr$F7v@=>`gBJor}SCX0iO9s zEC24^yw>?nt02T~pcAgFywABOt*_|3-B;uHe{Hk=|CK!R$KrFzm*UJTaSof`YKA1U zFbIHx0Td5AezBZ;7M1W|?wiP96C~$+EoMmH^?U0#26tnGXw?oqxd(A>Z)c|(G@xW`X87jwO${>-DF$RilI2iPBE_w9)Y^u4u6H z)1&4<%8b^Xzh`ct9Zt;uiTrRGGi^T8Td>iQ4hR zHc89+B^ZK_0k|yy2y+MF1{aw3-OE26jIjda#4HtkKf73Sb(t8<|^yc_DBTZMx zx;|Z??J-qz@B+10tGG5MsK^t<_}pQ!^*nLr(#&!K_RRt~^9qDY({_QfiOR47%jnB!Zuh}E`>(AGwhfCW=l8&ejY?&;QBh|N7;EbAGVZ*)iT)3Z+Y>ABE5guo7qrpWSFief(Gz8OT}e>nifPg} z8_5~o_386EBGrenj6)Ad(5=S#ZMG6HypD#%;-usj87Nw8x_%OjX4nC~bbDI8t=eXc zgq91{0-OVt#Rq`7vd-tUCkVfGZatm^xj&bR0mIds4?v5-+Mx}UcYgoFJ%vPdCvivW zTJ;Fs%!rlpEq;@%0tP^pQ%P+N3}Hvej|$Tzql4pF@CJ@3jYr&&d0po>+!M(w2ImUG z2KoA&{`>~}xIDW^aKB^oc;~N&EM51n^guVosml5^kHkv8;lqi})S^9cx>ZBvrMece z#318sbC@k$i)xwKp5Qm`aV+Y2uu_>$CJpZM8fbjeR*-I4D!EC9&~PNBsV(p3(KH7S z85B8J`8;iK#WhuLAdZv6BhH%_sh2nSH)oYe^2+=x#@=7wcrgi4{;WhojwduVi96O3 zep~i-)nmI`ycZ$qYsF*v0*~P(a^5zyqto-9rl~RI-y(vKV~KU%6wQI#Sl(mgoaLlx zqaut95#pNa z*7r`bbmc0)(ASZ20+H9JtZTZJ;}Un7ciJEeI+9}D1+-LR zf~2!O5|g^d6Xxb+>l`_83A@_0bRrXZ;u(7%1Z@}Us<0HKVYeNaM}6ep6kwjkA`tn{ zMIbDB!5G+R8Ul@30I}Qs8@@3_f7prX%WUCGERtKZ^4|wcxKxI1#n4V}9Xr)8>mf$x zVC_r|Dj{3%zPe>aoh*R&$)3T(Cows z{FWh<49$qGy$TuL&lR>2B6EKaIah*BuUc&C^yd+SAv&#i!bpa912ADN*aaX8#|%`5 pn8Yua(#u}6g<1FlULZp%AVRo*2&~&ZYterTqCSp^kcA}{{s*CJe9iy> literal 1528 zcma)+e@s(X6vxkNp#>}{%0SpMYO=7gxG510Qd$95t)OYLf5hz(U>P z;<6~gACN++n~|_=MTj^^VTGrO+mzBOD9Vri$dd9KN=o6)wzRtqom`ig?2nuK-n-|V zd(P*4&-?B}kuWmi^#}ln%-$pT2!Mtj2?z^pIZkmh+QJL=h>HN$Lba<%cyHGHN5=H?8j#DdYZ6nG z_HO?$PulBmNprFc??07v8EZ`DE&lD!HjXY;nTSQ%qzXjgNQwq6ZVTVlWqf-$-So_p zPPEcqBsS%YC;P?a53RiP32prEPt;8zGsmzC56VEM%l9kBN=X(4yzgTm^WUzwYxQdN7ri3Tt^f%bxknXr@OJA>>p0wfwWDic0Cgom;{gk7cP_dv;I@Y1 zn|#RVD6vY|pgbESckLIGmMIk-ScS+RQ<30J-~%v%SSJY(h|vn-ZDhiHVHp4mbtRBQ zKON5BTCF#S4K}+AATDf$_Ni>PRA;@NH|D!DRWbCyPv=!*B{ucJ(p;6P{o0O^Wl-~=%A#&Q-cvPH*}S_fU39gMxrynx)>TJ&RombXl7m8&A*^DHdJ zKp*7U%H3rB1OdrWD>Y8F$14{oqWc5P4rTGJqLD89>!i*oPx(flkxr(fs$aOIj8wQk zo8&{uBRWVo1o_94_ci^80Bd!CI$ruQ21MblK5hzONJal29U_hUUr7M67fJb;cUqUCUFgB9{DE>udHn6_6YroEkJ0 z+Sz+2OPBkr`R=!jbsRA4#xT{oKvNO>FeZhe?0SjlIANHXq}Z)Dm_TNt8AeL^$rR%; z3yn1HG*@VJQ9Ss8j93})z~SCJ00Z6u^u8Yx;UVWfDUAKN8ZL>C)PzkQ64+r8mKuVJ z`y)!fsS^O`!NQRFbqo=b?CCU2;SGcI+>s+?m+zXwzRG>I((B3?t)m^E4m;oU{z`p` zKvFc`y(w$wW-Aje2j%reXFOT6P<)_gpmRYl+_B#FE;b9>%q&nAVJ-ZxVit!A+kLS@ zZ^?*(b4m3(61b_cZmcy IcS^th2cLBUH2?qr From 05d229c5c75d011c19df5a40dfd42dc35346f348 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:33:31 -0500 Subject: [PATCH 04/16] bump version --- application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.fam b/application.fam index 9e1c81d..ed2cb13 100644 --- a/application.fam +++ b/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="assets", fap_description="@README.md", - fap_version="1.1", + fap_version="1.1.1", fap_author="Torron" ) From 30edc1b90781b9e5f4c890df9bfc298743557a8c Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:36:20 -0500 Subject: [PATCH 05/16] major/minor ver only --- application.fam | 2 +- key_copier.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application.fam b/application.fam index ed2cb13..a93326b 100644 --- a/application.fam +++ b/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="assets", fap_description="@README.md", - fap_version="1.1.1", + fap_version="1.2", fap_author="Torron" ) diff --git a/key_copier.c b/key_copier.c index 61ef3c9..5f9b7cc 100644 --- a/key_copier.c +++ b/key_copier.c @@ -675,7 +675,7 @@ static KeyCopierApp* key_copier_app_alloc() { app->dialogs = furi_record_open(RECORD_DIALOGS); app->file_path = furi_string_alloc(); app->submenu = submenu_alloc(); - submenu_set_header(app->submenu, "Key Copier v1.1"); + submenu_set_header(app->submenu, "Key Copier v1.2"); submenu_add_item( app->submenu, "Select Template", @@ -742,7 +742,7 @@ static KeyCopierApp* key_copier_app_alloc() { 0, 128, 64, - "Key Maker App 1.1\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " + "Key Maker App 1.2\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " "it on top of the screen.\n\n2. Use the contour to align your key.\n\n3. " "Adjust each pin's depth until they match. It's easier if you look with " "one eye closed.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial " From a86862eb252f4c8c57a56a537b4f569c9faa3817 Mon Sep 17 00:00:00 2001 From: Offreds Date: Tue, 4 Mar 2025 11:47:52 -0600 Subject: [PATCH 06/16] Update key_copier.c Added filtering by manufacturer --- key_copier.c | 148 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 47 deletions(-) diff --git a/key_copier.c b/key_copier.c index 5f9b7cc..62934d1 100644 --- a/key_copier.c +++ b/key_copier.c @@ -39,6 +39,8 @@ typedef enum { KeyCopierViewLoad, KeyCopierViewMeasure, KeyCopierViewAbout, + KeyCopierViewManufacturerList, + KeyCopierViewFormatList, } KeyCopierView; typedef struct { @@ -60,6 +62,9 @@ typedef struct { DialogsApp* dialogs; FuriString* file_path; + Submenu* manufacturer_list; + Submenu* format_list; + char* selected_manufacturer; } KeyCopierApp; typedef struct { @@ -127,56 +132,42 @@ void initialize_manufacturers(char** manufacturers) { } } -static void key_copier_format_change(VariableItem* item) { - KeyCopierApp* app = variable_item_get_context(item); - KeyCopierModel* model = view_get_model(app->view_measure); - if(model->data_loaded) { - variable_item_set_current_value_index(item, model->format_index); - } - uint8_t format_index = variable_item_get_current_value_index(item); - if(format_index != model->format_index) { - model->format_index = format_index; - model->format = all_formats[format_index]; - if(model->depth != NULL) { - free(model->depth); - } - model->depth = (uint8_t*)malloc((model->format.pin_num + 1) * sizeof(uint8_t)); - for(uint8_t i = 0; i <= model->format.pin_num; i++) { - model->depth[i] = model->format.min_depth_ind; - } - model->pin_slc = 1; - } - model->data_loaded = false; - variable_item_set_current_value_text(item, model->format.format_name); - variable_item_set_current_value_text(app->format_name_item, model->format.manufacturer); - model->format = all_formats[model->format_index]; -} +static void manufacturer_selected_callback(void* context, uint32_t index); +static void format_selected_callback(void* context, uint32_t index); + -static const char* format_config_label = "Key Format"; -static const char* format_name_config_label = "Brand"; static void key_copier_config_enter_callback(void* context) { KeyCopierApp* app = (KeyCopierApp*)context; - KeyCopierModel* my_model = view_get_model(app->view_measure); - variable_item_list_reset(app->variable_item_list_config); - // Recreate this view every time we enter it so that it's always updated - app->format_item = variable_item_list_add( - app->variable_item_list_config, - format_config_label, - COUNT_OF(all_formats), - key_copier_format_change, - app); + + // Clear manufacturer list + submenu_reset(app->manufacturer_list); + + // Track added manufacturers to avoid duplicates + char* added_manufacturers[COUNT_OF(all_formats)]; + size_t added_count = 0; + + // Add unique manufacturers + for(size_t i = 0; i < COUNT_OF(all_formats); i++) { + bool already_added = false; + for(size_t j = 0; j < added_count; j++) { + if(strcmp(all_formats[i].manufacturer, added_manufacturers[j]) == 0) { + already_added = true; + break; + } + } + + if(!already_added) { + submenu_add_item( + app->manufacturer_list, + all_formats[i].manufacturer, + i, + manufacturer_selected_callback, + app); + added_manufacturers[added_count++] = all_formats[i].manufacturer; + } + } - app->format_name_item = variable_item_list_add( - app->variable_item_list_config, format_name_config_label, 0, NULL, NULL); - View* view_config_i = variable_item_list_get_view(app->variable_item_list_config); - variable_item_set_current_value_index(app->format_item, my_model->format_index); - variable_item_set_current_value_text(app->format_name_item, my_model->format.manufacturer); - key_copier_format_change(app->format_item); - view_set_previous_callback(view_config_i, key_copier_navigation_submenu_callback); - view_dispatcher_remove_view( - app->view_dispatcher, KeyCopierViewConfigure_i); // delete the last one - view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_i, view_config_i); - view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewConfigure_i); // recreate it + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewManufacturerList); } static const char* key_name_entry_text = "Enter name"; @@ -664,6 +655,47 @@ static bool key_copier_view_measure_input_callback(InputEvent* event, void* cont return false; } +static void manufacturer_selected_callback(void* context, uint32_t index) { + KeyCopierApp* app = context; + app->selected_manufacturer = all_formats[index].manufacturer; + + // Clear and populate format list for selected manufacturer + submenu_reset(app->format_list); + + // Add all formats for this manufacturer + for(size_t i = 0; i < COUNT_OF(all_formats); i++) { + if(strcmp(all_formats[i].manufacturer, app->selected_manufacturer) == 0) { + submenu_add_item( + app->format_list, + all_formats[i].format_name, + i, + format_selected_callback, + app); + } + } + + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewFormatList); +} + +static void format_selected_callback(void* context, uint32_t index) { + KeyCopierApp* app = context; + KeyCopierModel* model = view_get_model(app->view_measure); + + model->format_index = index; + model->format = all_formats[index]; + if(model->depth != NULL) { + free(model->depth); + } + model->depth = malloc((model->format.pin_num + 1) * sizeof(uint8_t)); + for(uint8_t i = 0; i <= model->format.pin_num; i++) { + model->depth[i] = model->format.min_depth_ind; + } + model->pin_slc = 1; + model->data_loaded = false; + + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewMeasure); +} + static KeyCopierApp* key_copier_app_alloc() { KeyCopierApp* app = (KeyCopierApp*)malloc(sizeof(KeyCopierApp)); @@ -678,7 +710,7 @@ static KeyCopierApp* key_copier_app_alloc() { submenu_set_header(app->submenu, "Key Copier v1.2"); submenu_add_item( app->submenu, - "Select Template", + "Select Key Format", KeyCopierSubmenuIndexConfigure, key_copier_submenu_callback, app); @@ -752,6 +784,24 @@ static KeyCopierApp* key_copier_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, KeyCopierViewAbout, widget_get_view(app->widget_about)); + app->manufacturer_list = submenu_alloc(); + view_set_previous_callback( + submenu_get_view(app->manufacturer_list), + key_copier_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, + KeyCopierViewManufacturerList, + submenu_get_view(app->manufacturer_list)); + + app->format_list = submenu_alloc(); + view_set_previous_callback( + submenu_get_view(app->format_list), + key_copier_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, + KeyCopierViewFormatList, + submenu_get_view(app->format_list)); + app->notifications = furi_record_open(RECORD_NOTIFICATION); #ifdef BACKLIGHT_ON @@ -790,6 +840,10 @@ static void key_copier_app_free(KeyCopierApp* app) { variable_item_list_free(app->variable_item_list_config); view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewSubmenu); submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewManufacturerList); + submenu_free(app->manufacturer_list); + view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewFormatList); + submenu_free(app->format_list); view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); From f27221822129c062c95a5a752bb4d44808ca90b1 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 29 Mar 2025 07:39:10 -0400 Subject: [PATCH 07/16] Add TalkingSasquach's video QR --- assets/QR_Code.png | Bin 0 -> 1104 bytes key_copier.c | 152 ++++++++++++++++----------------------------- key_copier.h | 81 +++++++++++++++++++++++- 3 files changed, 133 insertions(+), 100 deletions(-) create mode 100644 assets/QR_Code.png diff --git a/assets/QR_Code.png b/assets/QR_Code.png new file mode 100644 index 0000000000000000000000000000000000000000..5997b8a20335dd51a0b9e02c3205a393273ebc43 GIT binary patch literal 1104 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1SD^?g<1e9mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5ln6+T@J#ddWzYh$IT)B2 zg&3HDEJh$?XwPI|0katxfF=O}^8!YM>;fjZtl|P@1RJD~W$rpTpdpc&ArU1JzCKpT z`MG+DDfvmMdKI|^AO#FI6;?oIZfZ%QLPc&)Ua?h$trA#;6_5=Q)>l#hD=EpgRf_Np zP;kyKN>wn^Gte_ovg1-vP_QXVNwW%aaf50H@@$ndN=gc>^!3Zj%k|2Q_413-^$jg8 zE%gnI^o@*kfhu&1EAvVcD|GXUl_7?}%yCIAPAoVA8 z`!KA2d?=zxl0okJ$Hcd6M=a;AWzc^i@imj}SuzXrj4Pk*&K@WZ`6Aidw`uW32_*|Q zKcS2N-&Vi-zW>Y}(?pBi)$$qg#`pfV@+xa~JafGtr?`8Y)hpjsi*udp*x5=~Z){?` zwBY(~E)Va6A7Z*E%>TBcM7HfFTg=R-JLLmsE{sUal)ljJr0&1kTln_UH_Mis;5)E0 zdFI01c8a`lUvdPE_OBF~yvuHjFWcu?Y!h}E|5G*VsHiD(+Uk3TXR)SzdGzItpJTc$ zRk>$Zdo+uf>?~*BtWjWgv*Nn&t=+#g)dE}JKI^Xt3^Mafo1vfo{ocO27ha3J5N+pU zuJk;XZ`{6oZkbY)2>-$@9T77GV^%x!z1KT6@8Ai!Hw?*dZwc}6x|W6AKJbaxgz3^G z#@Ds+?cWt`^%gBHlb*4NHSOm9U23yrXIIQ@*G`!>tB-5`q`%$rS<+1(PCwu?k`)m5 zKKmugyyMCxg_#YD6;h6zmizd-Qexpbonu=hH%=Bg5S%nWV&y_32Jgj(O>Ro~y5F6* zGj}tClw@HmLC26x%LAn!+E;;xvX -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "KeyCopier" - -#define BACKLIGHT_ON 1 - -typedef enum { - KeyCopierSubmenuIndexMeasure, - KeyCopierSubmenuIndexConfigure, - KeyCopierSubmenuIndexSave, - KeyCopierSubmenuIndexLoad, - KeyCopierSubmenuIndexAbout, -} KeyCopierSubmenuIndex; - -typedef enum { - KeyCopierViewSubmenu, - KeyCopierViewTextInput, - KeyCopierViewConfigure_i, - KeyCopierViewConfigure_e, - KeyCopierViewSave, - KeyCopierViewLoad, - KeyCopierViewMeasure, - KeyCopierViewAbout, - KeyCopierViewManufacturerList, - KeyCopierViewFormatList, -} KeyCopierView; - -typedef struct { - ViewDispatcher* view_dispatcher; - NotificationApp* notifications; - Submenu* submenu; - TextInput* text_input; - VariableItemList* variable_item_list_config; - View* view_measure; - View* view_config_e; - View* view_save; - View* view_load; - Widget* widget_about; - VariableItem* key_name_item; - VariableItem* format_item; - VariableItem* format_name_item; - char* temp_buffer; - uint32_t temp_buffer_size; - - DialogsApp* dialogs; - FuriString* file_path; - Submenu* manufacturer_list; - Submenu* format_list; - char* selected_manufacturer; -} KeyCopierApp; - -typedef struct { - uint32_t format_index; - FuriString* key_name_str; - uint8_t pin_slc; // The pin that is being adjusted - uint8_t* depth; // The cutting depth - bool data_loaded; - KeyFormat format; -} KeyCopierModel; + +void exit_widget_callback(GuiButtonType result, InputType type, void* context) { + KeyCopierApp* app = context; + UNUSED(result); + if(type == InputTypeShort) { + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu); + } +} void initialize_model(KeyCopierModel* model) { if(model->depth != NULL) { @@ -119,6 +51,9 @@ static void key_copier_submenu_callback(void* context, uint32_t index) { case KeyCopierSubmenuIndexAbout: view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewAbout); break; + case KeyCopierSubmenuIndexQRCode: + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewQRCode); + break; default: break; } @@ -135,17 +70,16 @@ void initialize_manufacturers(char** manufacturers) { static void manufacturer_selected_callback(void* context, uint32_t index); static void format_selected_callback(void* context, uint32_t index); - static void key_copier_config_enter_callback(void* context) { KeyCopierApp* app = (KeyCopierApp*)context; - + // Clear manufacturer list submenu_reset(app->manufacturer_list); - + // Track added manufacturers to avoid duplicates char* added_manufacturers[COUNT_OF(all_formats)]; size_t added_count = 0; - + // Add unique manufacturers for(size_t i = 0; i < COUNT_OF(all_formats); i++) { bool already_added = false; @@ -155,7 +89,7 @@ static void key_copier_config_enter_callback(void* context) { break; } } - + if(!already_added) { submenu_add_item( app->manufacturer_list, @@ -326,7 +260,7 @@ static void key_copier_view_measure_draw_callback(Canvas* canvas, void* model) { int bottom_post_extra_x_px = 0; // new int bottom_pre_extra_x_px = 0; // new int level_contour_px = - (int)round((my_format.last_pin_inch + my_format.elbow_inch) / inches_per_px); + (int)round((my_format.last_pin_inch + my_format.elbow_inch) / inches_per_px); for(int current_pin = 1; current_pin <= my_model->format.pin_num; current_pin += 1) { double current_center_px = my_format.first_pin_inch + (current_pin - 1) * my_format.pin_increment_inch; @@ -658,29 +592,25 @@ static bool key_copier_view_measure_input_callback(InputEvent* event, void* cont static void manufacturer_selected_callback(void* context, uint32_t index) { KeyCopierApp* app = context; app->selected_manufacturer = all_formats[index].manufacturer; - + // Clear and populate format list for selected manufacturer submenu_reset(app->format_list); - + // Add all formats for this manufacturer for(size_t i = 0; i < COUNT_OF(all_formats); i++) { if(strcmp(all_formats[i].manufacturer, app->selected_manufacturer) == 0) { submenu_add_item( - app->format_list, - all_formats[i].format_name, - i, - format_selected_callback, - app); + app->format_list, all_formats[i].format_name, i, format_selected_callback, app); } } - + view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewFormatList); } static void format_selected_callback(void* context, uint32_t index) { KeyCopierApp* app = context; KeyCopierModel* model = view_get_model(app->view_measure); - + model->format_index = index; model->format = all_formats[index]; if(model->depth != NULL) { @@ -722,6 +652,13 @@ static KeyCopierApp* key_copier_app_alloc() { app->submenu, "Load", KeyCopierSubmenuIndexLoad, key_copier_submenu_callback, app); submenu_add_item( app->submenu, "Help", KeyCopierSubmenuIndexAbout, key_copier_submenu_callback, app); + submenu_add_item( + app->submenu, + "Video Instruction", + KeyCopierSubmenuIndexQRCode, + key_copier_submenu_callback, + app); + view_set_previous_callback( submenu_get_view(app->submenu), key_copier_navigation_exit_callback); view_dispatcher_add_view( @@ -784,10 +721,28 @@ static KeyCopierApp* key_copier_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, KeyCopierViewAbout, widget_get_view(app->widget_about)); + app->widget_qr_code = widget_alloc(); + widget_add_icon_element(app->widget_qr_code, 92, 7, &I_QR_Code); + widget_add_string_element( + app->widget_qr_code, 0, 10, AlignLeft, AlignBottom, FontSecondary, "Check out"); + widget_add_string_element( + app->widget_qr_code, 0, 23, AlignLeft, AlignBottom, FontSecondary, "@TalkingSasquach's"); + widget_add_string_element( + app->widget_qr_code, 0, 36, AlignLeft, AlignBottom, FontSecondary, "video from decoding"); + widget_add_string_element( + app->widget_qr_code, 0, 49, AlignLeft, AlignBottom, FontSecondary, "a key to eventually"); + widget_add_string_element( + app->widget_qr_code, 0, 62, AlignLeft, AlignBottom, FontSecondary, "3D-printing a copy!"); + widget_add_button_element( + app->widget_qr_code, GuiButtonTypeRight, "Back", exit_widget_callback, app); + view_set_previous_callback( + widget_get_view(app->widget_qr_code), key_copier_navigation_submenu_callback); + view_dispatcher_add_view( + app->view_dispatcher, KeyCopierViewQRCode, widget_get_view(app->widget_qr_code)); + app->manufacturer_list = submenu_alloc(); view_set_previous_callback( - submenu_get_view(app->manufacturer_list), - key_copier_navigation_submenu_callback); + submenu_get_view(app->manufacturer_list), key_copier_navigation_submenu_callback); view_dispatcher_add_view( app->view_dispatcher, KeyCopierViewManufacturerList, @@ -795,17 +750,14 @@ static KeyCopierApp* key_copier_app_alloc() { app->format_list = submenu_alloc(); view_set_previous_callback( - submenu_get_view(app->format_list), - key_copier_navigation_submenu_callback); + submenu_get_view(app->format_list), key_copier_navigation_submenu_callback); view_dispatcher_add_view( - app->view_dispatcher, - KeyCopierViewFormatList, - submenu_get_view(app->format_list)); + app->view_dispatcher, KeyCopierViewFormatList, submenu_get_view(app->format_list)); app->notifications = furi_record_open(RECORD_NOTIFICATION); #ifdef BACKLIGHT_ON - notification_message(app->notifications, &sequence_display_backlight_enforce_on); + notification_message(app->notifications, &sequence_display_backlight_on); #endif return app; @@ -822,6 +774,8 @@ static void key_copier_app_free(KeyCopierApp* app) { free(app->temp_buffer); view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewAbout); widget_free(app->widget_about); + view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewQRCode); + widget_free(app->widget_qr_code); view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewMeasure); with_view_model( app->view_measure, diff --git a/key_copier.h b/key_copier.h index c2c8154..5411392 100644 --- a/key_copier.h +++ b/key_copier.h @@ -1,5 +1,84 @@ +#include "key_copier_icons.h" +#include "key_formats.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "KeyCopier" + +#define BACKLIGHT_ON 1 #define KEY_COPIER_FILE_EXTENSION ".keycopy" -#define INCHES_PER_PX 0.00978 +#define INCHES_PER_PX 0.00978 + +typedef enum { + KeyCopierSubmenuIndexMeasure, + KeyCopierSubmenuIndexConfigure, + KeyCopierSubmenuIndexSave, + KeyCopierSubmenuIndexLoad, + KeyCopierSubmenuIndexAbout, + KeyCopierSubmenuIndexQRCode, +} KeyCopierSubmenuIndex; + +typedef enum { + KeyCopierViewSubmenu, + KeyCopierViewTextInput, + KeyCopierViewConfigure_i, + KeyCopierViewConfigure_e, + KeyCopierViewSave, + KeyCopierViewLoad, + KeyCopierViewMeasure, + KeyCopierViewAbout, + KeyCopierViewQRCode, + KeyCopierViewManufacturerList, + KeyCopierViewFormatList, +} KeyCopierView; + +typedef struct { + ViewDispatcher* view_dispatcher; + NotificationApp* notifications; + Submenu* submenu; + TextInput* text_input; + VariableItemList* variable_item_list_config; + View* view_measure; + View* view_config_e; + View* view_save; + View* view_load; + Widget* widget_about; + Widget* widget_qr_code; + VariableItem* key_name_item; + VariableItem* format_item; + VariableItem* format_name_item; + char* temp_buffer; + uint32_t temp_buffer_size; + + DialogsApp* dialogs; + FuriString* file_path; + Submenu* manufacturer_list; + Submenu* format_list; + char* selected_manufacturer; +} KeyCopierApp; + +typedef struct { + uint32_t format_index; + FuriString* key_name_str; + uint8_t pin_slc; // The pin that is being adjusted + uint8_t* depth; // The cutting depth + bool data_loaded; + KeyFormat format; +} KeyCopierModel; static inline int min(int a, int b) { return (a < b) ? a : b; From 370d046b825a9810ede9a2696aaaab0c8be423bc Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 29 Mar 2025 07:54:40 -0400 Subject: [PATCH 08/16] prepare for publish. also fix format list exit callback --- CHANGELOG.md | 8 +++++++- README.md | 4 ++-- key_copier.c | 11 ++++++++--- screenshots/config.png | Bin 1527 -> 1623 bytes screenshots/main_menu.png | Bin 1765 -> 1804 bytes 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfbfc39..a1a992a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ +## 1.3 +* A new UI/workflow for key format selection by @Offreds's PR +* Added QR code directing user to @TalkingSasquach's awesome video from decoding keys to 3D-printing copies. + +## 1.2 +Bug fixes + ## 1.1 -## What's Changed * Support for double sided key and multiple new key formats by @HonestLocksmith * New formats: Manufacturer-Format Name-Data Sheet(if applicable) diff --git a/README.md b/README.md index 1347610..e141f72 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ To measure your key: ## Special Thanks - Thank [@jamisonderek](https://github.com/jamisonderek) for his [Flipper Zero Tutorial repository](https://github.com/jamisonderek/flipper-zero-tutorials) and [YouTube channel](https://github.com/jamisonderek/flipper-zero-tutorials#:~:text=YouTube%3A%20%40MrDerekJamison)! This app is built with his Skeleton App and GPIO Wiegand app as references. - Thank [@HonestLocksmith](https://github.com/HonestLocksmith) for PR #13 and #20. TONS of new key formats and supports for DOUBLE-SIDED keys are added. We have car keys now! +- Thank [@Offreds](https://github.com/Offreds) for PR #32. We now have a more streamlined workflow for key format selection. - Hey! We are on [Adam Savage's show](https://youtu.be/c8q2YVRiOAE?t=485)! Thanks for featuring my app! -- [Project channel](https://discord.com/channels/1112390971250974782/1264067969634402356) - +- We also made it to @TalkingSasquach's YouTube! He's made an awesome walkthrough from decoding the key bitting to 3D-printing copies. [Check it out!](https://www.youtube.com/watch?v=P3-KhSJE1as) diff --git a/key_copier.c b/key_copier.c index 0884259..7458aba 100644 --- a/key_copier.c +++ b/key_copier.c @@ -33,6 +33,11 @@ static uint32_t key_copier_navigation_submenu_callback(void* _context) { return KeyCopierViewSubmenu; } +static uint32_t key_copier_navigation_manufacturer_list_callback(void* _context) { + UNUSED(_context); + return KeyCopierViewManufacturerList; +} + static void key_copier_submenu_callback(void* context, uint32_t index) { KeyCopierApp* app = (KeyCopierApp*)context; switch(index) { @@ -637,7 +642,7 @@ static KeyCopierApp* key_copier_app_alloc() { app->dialogs = furi_record_open(RECORD_DIALOGS); app->file_path = furi_string_alloc(); app->submenu = submenu_alloc(); - submenu_set_header(app->submenu, "Key Copier v1.2"); + submenu_set_header(app->submenu, "Key Copier v1.3"); submenu_add_item( app->submenu, "Select Key Format", @@ -711,7 +716,7 @@ static KeyCopierApp* key_copier_app_alloc() { 0, 128, 64, - "Key Maker App 1.2\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " + "Key Maker App 1.3\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " "it on top of the screen.\n\n2. Use the contour to align your key.\n\n3. " "Adjust each pin's depth until they match. It's easier if you look with " "one eye closed.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial " @@ -750,7 +755,7 @@ static KeyCopierApp* key_copier_app_alloc() { app->format_list = submenu_alloc(); view_set_previous_callback( - submenu_get_view(app->format_list), key_copier_navigation_submenu_callback); + submenu_get_view(app->format_list), key_copier_navigation_manufacturer_list_callback); view_dispatcher_add_view( app->view_dispatcher, KeyCopierViewFormatList, submenu_get_view(app->format_list)); diff --git a/screenshots/config.png b/screenshots/config.png index fae7df6ac5980d7aedc99c379de3695d80b72321..2860e30dd645e52f7f53a656984e797419e607ce 100644 GIT binary patch literal 1623 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 diff --git a/screenshots/main_menu.png b/screenshots/main_menu.png index aad6a91604ebfeedc65e851b4211fe93160f6b57..3e804c0e6357cb92efb0eabbca2fcd02f3f3a974 100644 GIT binary patch literal 1804 zcmb7FZA?>V7=11vA8N%2Iv}JNGr`F^aEQumu@C{#Q0qpD1;i9Jf?|9vxn++~)qK zduTatmpDVF@I@P}nj1yyoO{^Rq-s_d*cB(oEP z6h(!$G@g;nrks5sYSs)UJQ>$7yQQq?q-8R^LmY|DpzKfkgm@)~G+GfU+M;sX8MXgcJ-3c_xth*haJ~2! zxLA!(YKp+_MvT1aneoSC!>cFQsKD+FY6#u#Zaw1-`~yyK+;a>jcnC@_{1(KtNLc#3v9Br+;9#6H%`N{09ZT9%zzj4bta)F02Bv> z&)rVoHD3orEK=!x)_{R!UXU(rFE7nxoHY9nWUMw= zOO0tpDb1RGyUs>w-x~;lu};}|kOZ2`=QFcEa7g(se<3Qey3>!!Lj7kx(4wsqv1D0*&;(hTXT8D~U9t|+lxf13f3h1s8pXbU; zSqUeRX|mxfjxA^WkED289O_0_P75YF-}0mpF`xT5YR-4vvtKRQRCJO!!QsH_{~zY# z*ttDOg-6rPObwz+I-3n-a8&xgxeZeLJ=AGka-z2)9aaB*B>zljavdeo!AlxH!88!0 zrsnjP9g~?OvJwH|dh%pjo@o`VI^+y5iASX7aNDyOU&>mJbg*;I=*$8g$7Mu~2-wLD zo+x5@*RNm`G9&HpVUcJ*2^*Ro*UGEgiMdz(_z^hye{CvZx_&;iQqJ%&-p&78uVkA8 zu7ptMAYSXw#?PMEiBvltiF=4{(lQQ)O;Auo^xKH_L7?bGo8s8m3dBsjU6>d#499xz zK<)>8HQTF@R7lTx-%HJ@?c>44a%9?-gC2DIsYmtqF)I;KkbNhEDl@o|M!#UDj8QIp zP2!7xcqwU{wgj5&r=+f}Ld{shjnp-;N;s|EBMv6noC&w%(8c?U%s)oqIv@oRuVKZ> zIG=^pFh~7|ZVsD(?}p4OyE_126!oos+O4xe7*ylGA9yH>mDmgTy%6+J@Lq5Bvk(PmW3e literal 1765 zcmZuye^8TU9RI#78Cb-U>(G@xW`X87jwO${>-DF$RilI2iPBE_w9)Y^u4u6H z)1&4<%8b^Xzh`ct9Zt;uiTrRGGi^T8Td>iQ4hR zHc89+B^ZK_0k|yy2y+MF1{aw3-OE26jIjda#4HtkKf73Sb(t8<|^yc_DBTZMx zx;|Z??J-qz@B+10tGG5MsK^t<_}pQ!^*nLr(#&!K_RRt~^9qDY({_QfiOR47%jnB!Zuh}E`>(AGwhfCW=l8&ejY?&;QBh|N7;EbAGVZ*)iT)3Z+Y>ABE5guo7qrpWSFief(Gz8OT}e>nifPg} z8_5~o_386EBGrenj6)Ad(5=S#ZMG6HypD#%;-usj87Nw8x_%OjX4nC~bbDI8t=eXc zgq91{0-OVt#Rq`7vd-tUCkVfGZatm^xj&bR0mIds4?v5-+Mx}UcYgoFJ%vPdCvivW zTJ;Fs%!rlpEq;@%0tP^pQ%P+N3}Hvej|$Tzql4pF@CJ@3jYr&&d0po>+!M(w2ImUG z2KoA&{`>~}xIDW^aKB^oc;~N&EM51n^guVosml5^kHkv8;lqi})S^9cx>ZBvrMece z#318sbC@k$i)xwKp5Qm`aV+Y2uu_>$CJpZM8fbjeR*-I4D!EC9&~PNBsV(p3(KH7S z85B8J`8;iK#WhuLAdZv6BhH%_sh2nSH)oYe^2+=x#@=7wcrgi4{;WhojwduVi96O3 zep~i-)nmI`ycZ$qYsF*v0*~P(a^5zyqto-9rl~RI-y(vKV~KU%6wQI#Sl(mgoaLlx zqaut95#pNa z*7r`bbmc0)(ASZ20+H9JtZTZJ;}Un7ciJEeI+9}D1+-LR zf~2!O5|g^d6Xxb+>l`_83A@_0bRrXZ;u(7%1Z@}Us<0HKVYeNaM}6ep6kwjkA`tn{ zMIbDB!5G+R8Ul@30I}Qs8@@3_f7prX%WUCGERtKZ^4|wcxKxI1#n4V}9Xr)8>mf$x zVC_r|Dj{3%zPe>aoh*R&$)3T(Cows z{FWh<49$qGy$TuL&lR>2B6EKaIah*BuUc&C^yd+SAv&#i!bpa912ADN*aaX8#|%`5 pn8Yua(#u}6g<1FlULZp%AVRo*2&~&ZYterTqCSp^kcA}{{s*CJe9iy> From b979d806908bbff541ecef9eef51565d32647788 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sun, 30 Mar 2025 06:34:39 -0400 Subject: [PATCH 09/16] Update application.fam --- application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application.fam b/application.fam index a93326b..e44c743 100644 --- a/application.fam +++ b/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="assets", fap_description="@README.md", - fap_version="1.2", + fap_version="1.3", fap_author="Torron" ) From f749d241afb993f73d8f7ac0de02a91087c8d514 Mon Sep 17 00:00:00 2001 From: Roberto Salgado Date: Fri, 27 Jun 2025 10:45:18 -0700 Subject: [PATCH 10/16] Add Weiser WR3 key format support - Added Weiser Classic (WR3) key format specifications - Updated FORMAT_NUM from 21 to 22 to accommodate new format - Specifications sourced from LockWiki documentation --- key_formats.c | 18 ++++++++++++++++++ key_formats.h | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/key_formats.c b/key_formats.c index cd5b632..32872e3 100644 --- a/key_formats.c +++ b/key_formats.c @@ -254,6 +254,24 @@ const KeyFormat all_formats[] = { .max_depth_ind = 6, .macs = 5, .clearance = 3}, + + {.manufacturer = "Weiser", + .format_name = "WR3", + .format_link = "https://www.lockwiki.com/index.php/Weiser_Classic", + .first_pin_inch = 0.237, + .last_pin_inch = 0.861, + .pin_increment_inch = 0.156, + .pin_num = 5, + .pin_width_inch = 0.090, + .elbow_inch = 0.150, + .drill_angle = 90, + .uncut_depth_inch = 0.315, + .deepest_depth_inch = 0.153, + .depth_step_inch = 0.018, + .min_depth_ind = 0, + .max_depth_ind = 10, + .macs = 6, + .clearance = 3}, {.manufacturer = "Ford", .format_name = "H75", diff --git a/key_formats.h b/key_formats.h index 67fb471..adda193 100644 --- a/key_formats.h +++ b/key_formats.h @@ -1,7 +1,7 @@ #ifndef KEY_FORMATS_H #define KEY_FORMATS_H -#define FORMAT_NUM 21 +#define FORMAT_NUM 22 typedef struct { char* manufacturer; From a40ceb512f32548c07c9d1f2325091b64f301be9 Mon Sep 17 00:00:00 2001 From: zinongli Date: Sun, 6 Jul 2025 15:36:54 -0400 Subject: [PATCH 11/16] prepare for v1.4 publish --- CHANGELOG.md | 3 +++ README.md | 1 + application.fam | 2 +- key_copier.c | 4 ++-- screenshots/main_menu.png | Bin 1804 -> 1800 bytes 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a992a..71fc817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.4 +* Added Weiser WR3 key format support by @lightos + ## 1.3 * A new UI/workflow for key format selection by @Offreds's PR * Added QR code directing user to @TalkingSasquach's awesome video from decoding keys to 3D-printing copies. diff --git a/README.md b/README.md index e141f72..348190f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ To measure your key: - Thank [@jamisonderek](https://github.com/jamisonderek) for his [Flipper Zero Tutorial repository](https://github.com/jamisonderek/flipper-zero-tutorials) and [YouTube channel](https://github.com/jamisonderek/flipper-zero-tutorials#:~:text=YouTube%3A%20%40MrDerekJamison)! This app is built with his Skeleton App and GPIO Wiegand app as references. - Thank [@HonestLocksmith](https://github.com/HonestLocksmith) for PR #13 and #20. TONS of new key formats and supports for DOUBLE-SIDED keys are added. We have car keys now! - Thank [@Offreds](https://github.com/Offreds) for PR #32. We now have a more streamlined workflow for key format selection. +- Thank [@lightos](https://github.com/lightos) for PR #36. Weiser WR3 key format was added. - Hey! We are on [Adam Savage's show](https://youtu.be/c8q2YVRiOAE?t=485)! Thanks for featuring my app! - We also made it to @TalkingSasquach's YouTube! He's made an awesome walkthrough from decoding the key bitting to 3D-printing copies. [Check it out!](https://www.youtube.com/watch?v=P3-KhSJE1as) diff --git a/application.fam b/application.fam index e44c743..d29f913 100644 --- a/application.fam +++ b/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="assets", fap_description="@README.md", - fap_version="1.3", + fap_version="1.4", fap_author="Torron" ) diff --git a/key_copier.c b/key_copier.c index 7458aba..ca3d430 100644 --- a/key_copier.c +++ b/key_copier.c @@ -642,7 +642,7 @@ static KeyCopierApp* key_copier_app_alloc() { app->dialogs = furi_record_open(RECORD_DIALOGS); app->file_path = furi_string_alloc(); app->submenu = submenu_alloc(); - submenu_set_header(app->submenu, "Key Copier v1.3"); + submenu_set_header(app->submenu, "Key Copier v1.4"); submenu_add_item( app->submenu, "Select Key Format", @@ -716,7 +716,7 @@ static KeyCopierApp* key_copier_app_alloc() { 0, 128, 64, - "Key Maker App 1.3\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " + "Key Maker App 1.4\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " "it on top of the screen.\n\n2. Use the contour to align your key.\n\n3. " "Adjust each pin's depth until they match. It's easier if you look with " "one eye closed.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial " diff --git a/screenshots/main_menu.png b/screenshots/main_menu.png index 3e804c0e6357cb92efb0eabbca2fcd02f3f3a974..b517aefcdd102f2382bb582d750ad8e6cb17a32f 100644 GIT binary patch literal 1800 zcmZvde^AnQ7{?!^5N$=5buCul)wOw7T$);%$k6PE)y%Roy(=n z)p==(cDE%OVy#%G1-WyrmWH!e5h13eAO=hJb2lj{q;?!-+DBB;qd1Y*7^8x04F?HekQmDBAgr?}Ih+VlA z&wFNA+XukQS z%Xqg+(zt&y9Nr9Yavs3N8Nl?z!Q3tX|2jD+ib%^pt&>Ko?p7(1T}7AU6zg@OA!UoL zvG^y^#Z!ykZXZmj;`;!rKEb)#KXgoHw*Vn*?*> zCD#me+CXYvL#e&KK0rQf?AhN&8(0*YKO(Bw&|AfB1BmnjajXaUYQJTO7uc*m`C;8BkE_bhQwR@0jJL}TbEpn%9n8Yu`{g~_of20{0Zd1}^~!0ADn_*G@4_R8ctlWV-P^?GBi@S#&g2_E)sR4Z z{Ebc%{#oyZ19zOm<{7GneVLlR?ylmhPaCQ__hfXAvNsm9)4IazRM}lGmi6E0 zv+JIyE%HITLo^CMNl4jv`cc#kJ+|NVeX8o9mm>zoOV^=RLM0h1^# zjXpTJ7e%qv0r6S`hn=YjT53xzRMN6bd?uT=)j%7XFi}gXqu%Z%gltXk{S1-f=;1H+J&RaYKE5pp#=k@KV962D4zuFmRt3WNdlJSP ziSU~zSdO4NuQS96MW)&@un3VF;zm=M=}TM!I`F6A>w-dZT>ZG#VlUSPTzdRU#Bp zl9J7P|MH{lUb2Utz>IJYq|f)^&l_qTPK_BCb_UmT7TwoIX11zdD%a(XNS&-4HLIb7 z=kZquvYEmC8AdK4tl00cDtB?Fw!HYj}j|c7bU~vLXZJ4-RLVooX+w2K9(R@m+xC-^cj>2GqxaJu$ z5hUTHBup`7; cxAYvOlMluxQu8I%*sl$uH^f9KB9o5)1GM{xtpET3 literal 1804 zcmb7FZA?>V7=11vA8N%2Iv}JNGr`F^aEQumu@C{#Q0qpD1;i9Jf?|9vxn++~)qK zduTatmpDVF@I@P}nj1yyoO{^Rq-s_d*cB(oEP z6h(!$G@g;nrks5sYSs)UJQ>$7yQQq?q-8R^LmY|DpzKfkgm@)~G+GfU+M;sX8MXgcJ-3c_xth*haJ~2! zxLA!(YKp+_MvT1aneoSC!>cFQsKD+FY6#u#Zaw1-`~yyK+;a>jcnC@_{1(KtNLc#3v9Br+;9#6H%`N{09ZT9%zzj4bta)F02Bv> z&)rVoHD3orEK=!x)_{R!UXU(rFE7nxoHY9nWUMw= zOO0tpDb1RGyUs>w-x~;lu};}|kOZ2`=QFcEa7g(se<3Qey3>!!Lj7kx(4wsqv1D0*&;(hTXT8D~U9t|+lxf13f3h1s8pXbU; zSqUeRX|mxfjxA^WkED289O_0_P75YF-}0mpF`xT5YR-4vvtKRQRCJO!!QsH_{~zY# z*ttDOg-6rPObwz+I-3n-a8&xgxeZeLJ=AGka-z2)9aaB*B>zljavdeo!AlxH!88!0 zrsnjP9g~?OvJwH|dh%pjo@o`VI^+y5iASX7aNDyOU&>mJbg*;I=*$8g$7Mu~2-wLD zo+x5@*RNm`G9&HpVUcJ*2^*Ro*UGEgiMdz(_z^hye{CvZx_&;iQqJ%&-p&78uVkA8 zu7ptMAYSXw#?PMEiBvltiF=4{(lQQ)O;Auo^xKH_L7?bGo8s8m3dBsjU6>d#499xz zK<)>8HQTF@R7lTx-%HJ@?c>44a%9?-gC2DIsYmtqF)I;KkbNhEDl@o|M!#UDj8QIp zP2!7xcqwU{wgj5&r=+f}Ld{shjnp-;N;s|EBMv6noC&w%(8c?U%s)oqIv@oRuVKZ> zIG=^pFh~7|ZVsD(?}p4OyE_126!oos+O4xe7*ylGA9yH>mDmgTy%6+J@Lq5Bvk(PmW3e From 49c2f557a4d0881a59eda35622cb8bae88d4cb72 Mon Sep 17 00:00:00 2001 From: RIcePatrol Date: Tue, 5 Aug 2025 18:55:58 -0400 Subject: [PATCH 12/16] Added Suzuki SUZ18 key type The Suzuki SUZ18 key is used on the 1998-2006 Katana among other models --- key_formats.c | 20 ++++++++++++++++++++ key_formats.h | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/key_formats.c b/key_formats.c index 32872e3..29d0721 100644 --- a/key_formats.c +++ b/key_formats.c @@ -356,6 +356,26 @@ const KeyFormat all_formats[] = { .macs = 4, .clearance = 3}, + {.manufacturer = "Suzuki", + .format_name = "SUZ18", + .sides = 2, + .format_link = "X241", + .first_pin_inch = 0.16, + .last_pin_inch = 0.73, + .pin_increment_inch = 0.095, + .pin_num = 7, + .pin_width_inch = 0.045, + .elbow_inch = 0.1, // this should be equal to first pin inch for tip + // stopped key line + .drill_angle = 90, + .uncut_depth_inch = 0.28, + .deepest_depth_inch = 0.22, + .depth_step_inch = 0.020, + .min_depth_ind = 1, + .max_depth_ind = 4, + .macs = 4, + .clearance = 4}, + {.manufacturer = "Yamaha", .format_name = "YM63", .sides = 2, diff --git a/key_formats.h b/key_formats.h index adda193..e60752d 100644 --- a/key_formats.h +++ b/key_formats.h @@ -1,7 +1,7 @@ #ifndef KEY_FORMATS_H #define KEY_FORMATS_H -#define FORMAT_NUM 22 +#define FORMAT_NUM 23 typedef struct { char* manufacturer; From 5f61be6d4c6c8c5087fbb253bed3197dfecdcffc Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:03:26 -0400 Subject: [PATCH 13/16] clearance fix --- key_formats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key_formats.c b/key_formats.c index 29d0721..17f408b 100644 --- a/key_formats.c +++ b/key_formats.c @@ -374,7 +374,7 @@ const KeyFormat all_formats[] = { .min_depth_ind = 1, .max_depth_ind = 4, .macs = 4, - .clearance = 4}, + .clearance = 3}, {.manufacturer = "Yamaha", .format_name = "YM63", From d9ac01225e36ca4b03cc401a22c41666f43bedbc Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:06:20 -0400 Subject: [PATCH 14/16] update version & changelog --- CHANGELOG.md | 3 +++ README.md | 1 + application.fam | 2 +- key_copier.c | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71fc817..b20626d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.5 +* Added Suzuki SUZ18 key format support by @RIcePatrol + ## 1.4 * Added Weiser WR3 key format support by @lightos diff --git a/README.md b/README.md index 348190f..e471cd0 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ To measure your key: - Thank [@HonestLocksmith](https://github.com/HonestLocksmith) for PR #13 and #20. TONS of new key formats and supports for DOUBLE-SIDED keys are added. We have car keys now! - Thank [@Offreds](https://github.com/Offreds) for PR #32. We now have a more streamlined workflow for key format selection. - Thank [@lightos](https://github.com/lightos) for PR #36. Weiser WR3 key format was added. +- Thank [@RIcePatrol](https://github.com/RIcePatrol) for PR #37. Suzuki SUZ18 key format was added. - Hey! We are on [Adam Savage's show](https://youtu.be/c8q2YVRiOAE?t=485)! Thanks for featuring my app! - We also made it to @TalkingSasquach's YouTube! He's made an awesome walkthrough from decoding the key bitting to 3D-printing copies. [Check it out!](https://www.youtube.com/watch?v=P3-KhSJE1as) diff --git a/application.fam b/application.fam index d29f913..ab46b67 100644 --- a/application.fam +++ b/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="assets", fap_description="@README.md", - fap_version="1.4", + fap_version="1.5", fap_author="Torron" ) diff --git a/key_copier.c b/key_copier.c index ca3d430..7cc0462 100644 --- a/key_copier.c +++ b/key_copier.c @@ -642,7 +642,7 @@ static KeyCopierApp* key_copier_app_alloc() { app->dialogs = furi_record_open(RECORD_DIALOGS); app->file_path = furi_string_alloc(); app->submenu = submenu_alloc(); - submenu_set_header(app->submenu, "Key Copier v1.4"); + submenu_set_header(app->submenu, "Key Copier v1.5"); submenu_add_item( app->submenu, "Select Key Format", @@ -716,7 +716,7 @@ static KeyCopierApp* key_copier_app_alloc() { 0, 128, 64, - "Key Maker App 1.4\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " + "Key Maker App 1.5\nAuthor: @Torron\n\nTo measure your key:\n\n1. Place " "it on top of the screen.\n\n2. Use the contour to align your key.\n\n3. " "Adjust each pin's depth until they match. It's easier if you look with " "one eye closed.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial " From 9524c98675d8b118774dd4bbf9e0492cce0dd5fb Mon Sep 17 00:00:00 2001 From: H4W9 Date: Fri, 2 Jan 2026 19:24:03 -0600 Subject: [PATCH 15/16] Update key_formats.c Add KW5 (Kwikset) and SC1 (Schlage) --- key_formats.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/key_formats.c b/key_formats.c index 17f408b..6e76188 100644 --- a/key_formats.c +++ b/key_formats.c @@ -20,6 +20,43 @@ const KeyFormat all_formats[] = { .macs = 4, .clearance = 3}, + {.manufacturer = "Kwikset", + .format_name = "KW5", + .format_link = "https://lsamichigan.org/Tech/Kwikset_KeySpecs.pdf", + .first_pin_inch = 0.247, + .last_pin_inch = 0.997, + .pin_increment_inch = 0.15, + .pin_num = 6, + .pin_width_inch = 0.084, + .elbow_inch = 0.15, + .drill_angle = 90, + .uncut_depth_inch = 0.329, + .deepest_depth_inch = 0.191, + .depth_step_inch = 0.023, + .min_depth_ind = 1, + .max_depth_ind = 7, + .macs = 4, + .clearance = 3}, + + {.manufacturer = "Schlage", + .format_name = "SC1", + .format_link = "https://lsamichigan.org/Tech/SCHLAGE_KeySpecs.pdf", + .first_pin_inch = 0.231, + .last_pin_inch = 0.8558, + .pin_increment_inch = 0.1562, + .pin_num = 5, + .pin_width_inch = 0.031, + .elbow_inch = 0.1, + .drill_angle = 90, // This should actually be 100 but the current resolution will make + // 100 degrees very ugly and unsuable + .uncut_depth_inch = 0.335, + .deepest_depth_inch = 0.2, + .depth_step_inch = 0.015, + .min_depth_ind = 0, + .max_depth_ind = 9, + .macs = 7, + .clearance = 8}, + {.manufacturer = "Schlage", .format_name = "SC4", .format_link = "https://lsamichigan.org/Tech/SCHLAGE_KeySpecs.pdf", @@ -435,3 +472,4 @@ const KeyFormat all_formats[] = { .max_depth_ind = 3, .macs = 3, .clearance = 1}}; + From da5310fed67ba25baf7d02dff7676122cc68962c Mon Sep 17 00:00:00 2001 From: H4W9 Date: Fri, 2 Jan 2026 20:03:18 -0600 Subject: [PATCH 16/16] Update key_formats.h correct FORMAT_NUM --- key_formats.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/key_formats.h b/key_formats.h index e60752d..92a8de7 100644 --- a/key_formats.h +++ b/key_formats.h @@ -1,7 +1,7 @@ #ifndef KEY_FORMATS_H #define KEY_FORMATS_H -#define FORMAT_NUM 23 +#define FORMAT_NUM 25 typedef struct { char* manufacturer; @@ -30,3 +30,4 @@ typedef struct { extern const KeyFormat all_formats[FORMAT_NUM]; #endif // KEY_FORMATS_H +