|
@@ -1,41 +1,75 @@
|
|
|
#include <furi.h>
|
|
#include <furi.h>
|
|
|
|
|
+#include <furi_hal.h>
|
|
|
#include <gui/gui.h>
|
|
#include <gui/gui.h>
|
|
|
|
|
+#include <gui/view.h>
|
|
|
|
|
+#include <gui/view_dispatcher.h>
|
|
|
|
|
+#include <gui/modules/submenu.h>
|
|
|
|
|
+#include <gui/modules/widget.h>
|
|
|
|
|
+#include <notification/notification.h>
|
|
|
|
|
+#include <notification/notification_messages.h>
|
|
|
#include <input/input.h>
|
|
#include <input/input.h>
|
|
|
-#include <stdlib.h>
|
|
|
|
|
-#include <string.h>
|
|
|
|
|
-#include <stdio.h>
|
|
|
|
|
|
|
+#include "combo_cracker_icons.h"
|
|
|
|
|
|
|
|
-#define SCREEN_HEIGHT 64
|
|
|
|
|
-#define MAX_VALUES 10
|
|
|
|
|
|
|
+#define TAG "ComboLockCracker"
|
|
|
|
|
+
|
|
|
|
|
+#define BACKLIGHT_ON 1
|
|
|
|
|
+
|
|
|
|
|
+#define MAX_VALUES 10
|
|
|
|
|
+
|
|
|
|
|
+// thank you derek jamison! ;)
|
|
|
|
|
+typedef enum {
|
|
|
|
|
+ ComboViewSubmenu,
|
|
|
|
|
+ ComboViewCracker,
|
|
|
|
|
+ ComboViewResults,
|
|
|
|
|
+ ComboViewAbout,
|
|
|
|
|
+} ComboView;
|
|
|
|
|
+
|
|
|
|
|
+typedef enum {
|
|
|
|
|
+ ComboEventIdRedrawScreen = 0,
|
|
|
|
|
+ ComboEventIdCalculateCombo = 1,
|
|
|
|
|
+} ComboEventId;
|
|
|
|
|
|
|
|
typedef enum {
|
|
typedef enum {
|
|
|
- SCREEN_COMBO_ENTRY,
|
|
|
|
|
- SCREEN_RESULTS,
|
|
|
|
|
- SCREEN_ABOUT
|
|
|
|
|
-} ScreenState;
|
|
|
|
|
|
|
+ ComboSubmenuIndexCracker,
|
|
|
|
|
+ ComboSubmenuIndexAbout,
|
|
|
|
|
+} ComboSubmenuIndex;
|
|
|
|
|
+
|
|
|
|
|
+typedef struct {
|
|
|
|
|
+ ViewDispatcher* view_dispatcher;
|
|
|
|
|
+ NotificationApp* notifications;
|
|
|
|
|
+ Submenu* submenu;
|
|
|
|
|
+ View* view_cracker;
|
|
|
|
|
+ Widget* widget_results;
|
|
|
|
|
+ Widget* widget_about;
|
|
|
|
|
+
|
|
|
|
|
+ FuriTimer* timer;
|
|
|
|
|
+} ComboLockCrackerApp;
|
|
|
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
int first_lock;
|
|
int first_lock;
|
|
|
int second_lock;
|
|
int second_lock;
|
|
|
float resistance;
|
|
float resistance;
|
|
|
- bool exit;
|
|
|
|
|
int selected;
|
|
int selected;
|
|
|
char result[256];
|
|
char result[256];
|
|
|
- ViewPort* view_port;
|
|
|
|
|
- Gui* gui;
|
|
|
|
|
- ScreenState screen_state;
|
|
|
|
|
-} ComboCrackerState;
|
|
|
|
|
-
|
|
|
|
|
-char* float_to_char(float num) {
|
|
|
|
|
|
|
+} ComboLockCrackerModel;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief helper to convert float to string
|
|
|
|
|
+ * @param num the float number to convert
|
|
|
|
|
+ * @return string representation of the float
|
|
|
|
|
+ */
|
|
|
|
|
+static char* float_to_char(float num) {
|
|
|
static char buffer[8];
|
|
static char buffer[8];
|
|
|
snprintf(buffer, sizeof(buffer), "%.1f", (double)num);
|
|
snprintf(buffer, sizeof(buffer), "%.1f", (double)num);
|
|
|
return buffer;
|
|
return buffer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// calculate the value for the combo lock
|
|
|
|
|
-// Samy is my hero ;) -> https://www.youtube.com/watch?v=qkolWO6pAL8
|
|
|
|
|
-void calculate_combo(ComboCrackerState* app) {
|
|
|
|
|
- float sticky_number = app->resistance;
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief calculate the combination based on inputs
|
|
|
|
|
+ * @param model the model containing input values
|
|
|
|
|
+ */
|
|
|
|
|
+static void calculate_combo(ComboLockCrackerModel* model) {
|
|
|
|
|
+ float sticky_number = model->resistance;
|
|
|
int sticky_as_int = (int)sticky_number;
|
|
int sticky_as_int = (int)sticky_number;
|
|
|
|
|
|
|
|
int first_digit;
|
|
int first_digit;
|
|
@@ -49,8 +83,8 @@ void calculate_combo(ComboCrackerState* app) {
|
|
|
first_digit = first_digit % 40;
|
|
first_digit = first_digit % 40;
|
|
|
int remainder = first_digit % 4;
|
|
int remainder = first_digit % 4;
|
|
|
|
|
|
|
|
- int a = app->first_lock;
|
|
|
|
|
- int b = app->second_lock;
|
|
|
|
|
|
|
+ int a = model->first_lock;
|
|
|
|
|
+ int b = model->second_lock;
|
|
|
|
|
|
|
|
// third digit isn't too bad
|
|
// third digit isn't too bad
|
|
|
int third_position_values[MAX_VALUES];
|
|
int third_position_values[MAX_VALUES];
|
|
@@ -78,7 +112,6 @@ void calculate_combo(ComboCrackerState* app) {
|
|
|
second_position_values[second_count++] = row_2;
|
|
second_position_values[second_count++] = row_2;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // sort that biih
|
|
|
|
|
for(int i = 0; i < second_count - 1; i++) {
|
|
for(int i = 0; i < second_count - 1; i++) {
|
|
|
for(int j = i + 1; j < second_count; j++) {
|
|
for(int j = i + 1; j < second_count; j++) {
|
|
|
if(second_position_values[i] > second_position_values[j]) {
|
|
if(second_position_values[i] > second_position_values[j]) {
|
|
@@ -89,174 +122,334 @@ void calculate_combo(ComboCrackerState* app) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // result to push to result_callback
|
|
|
|
|
- snprintf(app->result, sizeof(app->result), "First Pin: %d\nSecond Pin(s): ", first_digit);
|
|
|
|
|
|
|
+ int pos = 0;
|
|
|
|
|
+ int written = snprintf(
|
|
|
|
|
+ model->result + pos,
|
|
|
|
|
+ sizeof(model->result) - pos,
|
|
|
|
|
+ "First Pin: %d\nSecond Pin(s): ",
|
|
|
|
|
+ first_digit);
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+
|
|
|
for(int i = 0; i < second_count; i++) {
|
|
for(int i = 0; i < second_count; i++) {
|
|
|
- char buf[6];
|
|
|
|
|
- snprintf(buf, sizeof(buf), "%d", second_position_values[i]);
|
|
|
|
|
- strcat(app->result, buf);
|
|
|
|
|
- if(i < second_count - 1) strcat(app->result, ", ");
|
|
|
|
|
- if(i == 3) strcat(app->result, "\n -> ");
|
|
|
|
|
|
|
+ written = snprintf(
|
|
|
|
|
+ model->result + pos, sizeof(model->result) - pos, "%d", second_position_values[i]);
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+
|
|
|
|
|
+ if(i < second_count - 1) {
|
|
|
|
|
+ const char* sep = (i == 3) ? ",\n -> " : ", ";
|
|
|
|
|
+ written = snprintf(model->result + pos, sizeof(model->result) - pos, "%s", sep);
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- strcat(app->result, "\nThird Pin(s): ");
|
|
|
|
|
|
|
+ written = snprintf(model->result + pos, sizeof(model->result) - pos, "\nThird Pin(s): ");
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+
|
|
|
for(int i = 0; i < third_count; i++) {
|
|
for(int i = 0; i < third_count; i++) {
|
|
|
- char buf[5];
|
|
|
|
|
- snprintf(buf, sizeof(buf), "%d", third_position_values[i]);
|
|
|
|
|
- strcat(app->result, buf);
|
|
|
|
|
- if(i < third_count - 1) strcat(app->result, ", "); // may be more than one sometimes
|
|
|
|
|
- // deduce to 8 attempts by popping -> (third_digit +/- 2)
|
|
|
|
|
|
|
+ written = snprintf(
|
|
|
|
|
+ model->result + pos, sizeof(model->result) - pos, "%d", third_position_values[i]);
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+
|
|
|
|
|
+ if(i < third_count - 1) {
|
|
|
|
|
+ written = snprintf(model->result + pos, sizeof(model->result) - pos, ", ");
|
|
|
|
|
+ if(written < 0 || written >= (int)(sizeof(model->result) - pos)) return;
|
|
|
|
|
+ pos += written;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// main screen to push the first, second, and resistance positions
|
|
|
|
|
-void draw_callback(Canvas* canvas, void* ctx) {
|
|
|
|
|
- ComboCrackerState* app = ctx;
|
|
|
|
|
-
|
|
|
|
|
- canvas_clear(canvas);
|
|
|
|
|
- canvas_set_font(canvas, FontPrimary);
|
|
|
|
|
-
|
|
|
|
|
- char buf[16];
|
|
|
|
|
-
|
|
|
|
|
- canvas_draw_str(canvas, 2, 12, "First Lock:");
|
|
|
|
|
- snprintf(buf, sizeof(buf), "%s%d", app->selected == 0 ? ">" : " ", app->first_lock);
|
|
|
|
|
- canvas_draw_str(canvas, 100, 12, buf);
|
|
|
|
|
-
|
|
|
|
|
- canvas_draw_str(canvas, 2, 24, "Second Lock:");
|
|
|
|
|
- snprintf(buf, sizeof(buf), "%s%d", app->selected == 1 ? ">" : " ", app->second_lock);
|
|
|
|
|
- canvas_draw_str(canvas, 100, 24, buf);
|
|
|
|
|
-
|
|
|
|
|
- canvas_draw_str(canvas, 2, 36, "Resistance:");
|
|
|
|
|
- snprintf(
|
|
|
|
|
- buf, sizeof(buf), "%s%s", app->selected == 2 ? ">" : " ", float_to_char(app->resistance));
|
|
|
|
|
- canvas_draw_str(canvas, 100, 36, buf);
|
|
|
|
|
-
|
|
|
|
|
- snprintf(
|
|
|
|
|
- buf, sizeof(buf), "%sAbout", app->selected == 3 ? ">" : " "); // ugly but we rollin wit it
|
|
|
|
|
- canvas_draw_str(canvas, 2, 48, buf);
|
|
|
|
|
-
|
|
|
|
|
- canvas_draw_str(canvas, 2, 62, "OK to calculate"); // is there an OK icon??
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief callback for exiting the application.
|
|
|
|
|
+ * @details this function is called when user press back button.
|
|
|
|
|
+ * @param _context the context - unused
|
|
|
|
|
+ * @return next view id
|
|
|
|
|
+ */
|
|
|
|
|
+static uint32_t combo_navigation_exit_callback(void* _context) {
|
|
|
|
|
+ UNUSED(_context);
|
|
|
|
|
+ return VIEW_NONE;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// push the calc to screen
|
|
|
|
|
-void result_draw_callback(Canvas* canvas, void* ctx) {
|
|
|
|
|
- ComboCrackerState* app = ctx;
|
|
|
|
|
- canvas_clear(canvas);
|
|
|
|
|
- canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
-
|
|
|
|
|
- int y = 12;
|
|
|
|
|
- char result_copy[256];
|
|
|
|
|
- strncpy(result_copy, app->result, sizeof(result_copy));
|
|
|
|
|
- char* line = strtok(result_copy, "\n");
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief callback for returning to submenu.
|
|
|
|
|
+ * @details this function is called when user press back button.
|
|
|
|
|
+ * @param _context the context - unused
|
|
|
|
|
+ * @return next view id
|
|
|
|
|
+ */
|
|
|
|
|
+static uint32_t combo_navigation_submenu_callback(void* _context) {
|
|
|
|
|
+ UNUSED(_context);
|
|
|
|
|
+ return ComboViewSubmenu;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- while(line && y < SCREEN_HEIGHT - 10) {
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, line);
|
|
|
|
|
- y += 10;
|
|
|
|
|
- line = strtok(NULL, "\n");
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief handle submenu item selection.
|
|
|
|
|
+ * @details this function is called when user selects an item from the submenu.
|
|
|
|
|
+ * @param context the context - ComboLockCrackerApp object.
|
|
|
|
|
+ * @param index the ComboSubmenuIndex item that was clicked.
|
|
|
|
|
+ */
|
|
|
|
|
+static void combo_submenu_callback(void* context, uint32_t index) {
|
|
|
|
|
+ ComboLockCrackerApp* app = (ComboLockCrackerApp*)context;
|
|
|
|
|
+ switch(index) {
|
|
|
|
|
+ case ComboSubmenuIndexCracker:
|
|
|
|
|
+ view_dispatcher_switch_to_view(app->view_dispatcher, ComboViewCracker);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ComboSubmenuIndexAbout:
|
|
|
|
|
+ view_dispatcher_switch_to_view(app->view_dispatcher, ComboViewAbout);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- canvas_draw_str(canvas, 2, SCREEN_HEIGHT - 2, "Back to edit");
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void about_draw_callback(Canvas* canvas, void* ctx) {
|
|
|
|
|
- UNUSED(ctx);
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief callback for drawing the cracker screen.
|
|
|
|
|
+ * @details this function is called when the screen needs to be redrawn.
|
|
|
|
|
+ * @param canvas the canvas to draw on.
|
|
|
|
|
+ * @param model the model - ComboLockCrackerModel object.
|
|
|
|
|
+ */
|
|
|
|
|
+static void combo_view_cracker_draw_callback(Canvas* canvas, void* model) {
|
|
|
|
|
+ ComboLockCrackerModel* my_model = (ComboLockCrackerModel*)model;
|
|
|
|
|
+
|
|
|
canvas_clear(canvas);
|
|
canvas_clear(canvas);
|
|
|
canvas_set_font(canvas, FontSecondary);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
|
|
|
|
|
- int y = 12;
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, "Combo Lock Cracker");
|
|
|
|
|
- y += 10;
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, "Based on Samy Kamkar's");
|
|
|
|
|
- y += 10;
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, "Master Lock research.");
|
|
|
|
|
- y += 10;
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, "Crack Combo Locks in 8 tries");
|
|
|
|
|
- y += 10;
|
|
|
|
|
- canvas_draw_str(canvas, 2, y, "https://samy.pl/master/");
|
|
|
|
|
- y += 18;
|
|
|
|
|
- canvas_draw_str(canvas, 2, SCREEN_HEIGHT - 2, "Back to main menu");
|
|
|
|
|
|
|
+ char buf[16];
|
|
|
|
|
+ int icon_width = 32;
|
|
|
|
|
+ int icon_x = 128 - icon_width - 2; // moved 3 pixels to the right
|
|
|
|
|
+ int icon_y = 2;
|
|
|
|
|
+ int text_x = 2;
|
|
|
|
|
+ int value_x = 75;
|
|
|
|
|
+ int indicator_offset = -5;
|
|
|
|
|
+
|
|
|
|
|
+ canvas_draw_str(canvas, text_x, 12, "First Lock:");
|
|
|
|
|
+ snprintf(buf, sizeof(buf), "%d", my_model->first_lock);
|
|
|
|
|
+ canvas_draw_str(
|
|
|
|
|
+ canvas,
|
|
|
|
|
+ value_x + (my_model->selected == 0 ? indicator_offset : 0),
|
|
|
|
|
+ 12,
|
|
|
|
|
+ (my_model->selected == 0 ? ">" : ""));
|
|
|
|
|
+ canvas_draw_str(canvas, value_x, 12, buf);
|
|
|
|
|
+
|
|
|
|
|
+ canvas_draw_str(canvas, text_x, 24, "Second Lock:");
|
|
|
|
|
+ snprintf(buf, sizeof(buf), "%d", my_model->second_lock);
|
|
|
|
|
+ canvas_draw_str(
|
|
|
|
|
+ canvas,
|
|
|
|
|
+ value_x + (my_model->selected == 1 ? indicator_offset : 0),
|
|
|
|
|
+ 24,
|
|
|
|
|
+ (my_model->selected == 1 ? ">" : ""));
|
|
|
|
|
+ canvas_draw_str(canvas, value_x, 24, buf);
|
|
|
|
|
+
|
|
|
|
|
+ canvas_draw_str(canvas, text_x, 36, "Resistance:");
|
|
|
|
|
+ snprintf(buf, sizeof(buf), "%s", float_to_char(my_model->resistance));
|
|
|
|
|
+ canvas_draw_str(
|
|
|
|
|
+ canvas,
|
|
|
|
|
+ value_x + (my_model->selected == 2 ? indicator_offset : 0),
|
|
|
|
|
+ 36,
|
|
|
|
|
+ (my_model->selected == 2 ? ">" : ""));
|
|
|
|
|
+ canvas_draw_str(canvas, value_x, 36, buf);
|
|
|
|
|
+
|
|
|
|
|
+ canvas_draw_str(canvas, text_x, 62, "OK to calculate");
|
|
|
|
|
+ canvas_draw_icon(canvas, icon_x, icon_y, &I_lock32x32);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void input_callback(InputEvent* event, void* ctx);
|
|
|
|
|
-void result_input_callback(InputEvent* event, void* ctx) {
|
|
|
|
|
- ComboCrackerState* app = ctx;
|
|
|
|
|
- if(event->type == InputTypeShort && event->key == InputKeyBack) {
|
|
|
|
|
- app->screen_state = SCREEN_COMBO_ENTRY;
|
|
|
|
|
- view_port_draw_callback_set(app->view_port, draw_callback, app);
|
|
|
|
|
- view_port_input_callback_set(app->view_port, input_callback, app);
|
|
|
|
|
- }
|
|
|
|
|
- view_port_update(app->view_port);
|
|
|
|
|
-}
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief callback for cracker screen input.
|
|
|
|
|
+ * @details this function is called when the user presses a button while on the cracker screen.
|
|
|
|
|
+ * @param event the event - InputEvent object.
|
|
|
|
|
+ * @param context the context - ComboLockCrackerApp object.
|
|
|
|
|
+ * @return true if the event was handled, false otherwise.
|
|
|
|
|
+ */
|
|
|
|
|
+static bool combo_view_cracker_input_callback(InputEvent* event, void* context) {
|
|
|
|
|
+ ComboLockCrackerApp* app = (ComboLockCrackerApp*)context;
|
|
|
|
|
|
|
|
-void input_callback(InputEvent* event, void* ctx) {
|
|
|
|
|
- ComboCrackerState* app = ctx;
|
|
|
|
|
if(event->type == InputTypeShort) {
|
|
if(event->type == InputTypeShort) {
|
|
|
|
|
+ bool redraw = true;
|
|
|
switch(event->key) {
|
|
switch(event->key) {
|
|
|
case InputKeyUp:
|
|
case InputKeyUp:
|
|
|
- app->selected = (app->selected + 3) % 4;
|
|
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker,
|
|
|
|
|
+ ComboLockCrackerModel * model,
|
|
|
|
|
+ { model->selected = (model->selected > 0) ? model->selected - 1 : 2; },
|
|
|
|
|
+ redraw);
|
|
|
break;
|
|
break;
|
|
|
case InputKeyDown:
|
|
case InputKeyDown:
|
|
|
- app->selected = (app->selected + 1) % 4;
|
|
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker,
|
|
|
|
|
+ ComboLockCrackerModel * model,
|
|
|
|
|
+ { model->selected = (model->selected < 2) ? model->selected + 1 : 0; },
|
|
|
|
|
+ redraw);
|
|
|
break;
|
|
break;
|
|
|
case InputKeyLeft:
|
|
case InputKeyLeft:
|
|
|
- if(app->selected == 0 && app->first_lock > 0) app->first_lock--;
|
|
|
|
|
- if(app->selected == 1 && app->second_lock > 0) app->second_lock--;
|
|
|
|
|
- if(app->selected == 2 && app->resistance > 0) app->resistance -= 0.5;
|
|
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker,
|
|
|
|
|
+ ComboLockCrackerModel * model,
|
|
|
|
|
+ {
|
|
|
|
|
+ if(model->selected == 0 && model->first_lock > 0) model->first_lock--;
|
|
|
|
|
+ if(model->selected == 1 && model->second_lock > 0) model->second_lock--;
|
|
|
|
|
+ if(model->selected == 2 && model->resistance > 0) model->resistance -= 0.5f;
|
|
|
|
|
+ },
|
|
|
|
|
+ redraw);
|
|
|
break;
|
|
break;
|
|
|
- case InputKeyRight: // can't figure out how to accomodate for long presses for fast incrementation??
|
|
|
|
|
- if(app->selected == 0 && app->first_lock < 10) app->first_lock++;
|
|
|
|
|
- if(app->selected == 1 && app->second_lock < 10) app->second_lock++;
|
|
|
|
|
- if(app->selected == 2 && app->resistance < 39.5) app->resistance += 0.5;
|
|
|
|
|
- if(app->selected == 3) { // about description
|
|
|
|
|
- app->screen_state = SCREEN_ABOUT;
|
|
|
|
|
- view_port_draw_callback_set(app->view_port, about_draw_callback, app);
|
|
|
|
|
- view_port_input_callback_set(app->view_port, result_input_callback, app);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ case InputKeyRight:
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker,
|
|
|
|
|
+ ComboLockCrackerModel * model,
|
|
|
|
|
+ {
|
|
|
|
|
+ if(model->selected == 0 && model->first_lock < 39) model->first_lock++;
|
|
|
|
|
+ if(model->selected == 1 && model->second_lock < 39) model->second_lock++;
|
|
|
|
|
+ if(model->selected == 2 && model->resistance < 39.5f)
|
|
|
|
|
+ model->resistance += 0.5f;
|
|
|
|
|
+ },
|
|
|
|
|
+ redraw);
|
|
|
break;
|
|
break;
|
|
|
case InputKeyOk:
|
|
case InputKeyOk:
|
|
|
- calculate_combo(app);
|
|
|
|
|
- app->screen_state = SCREEN_RESULTS;
|
|
|
|
|
- view_port_draw_callback_set(app->view_port, result_draw_callback, app);
|
|
|
|
|
- view_port_input_callback_set(app->view_port, result_input_callback, app);
|
|
|
|
|
- break;
|
|
|
|
|
- case InputKeyBack:
|
|
|
|
|
- app->exit = true;
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ view_dispatcher_send_custom_event(app->view_dispatcher, ComboEventIdCalculateCombo);
|
|
|
|
|
+ return true;
|
|
|
default:
|
|
default:
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- view_port_update(app->view_port);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-int32_t combo_cracker_app(void* p) {
|
|
|
|
|
- UNUSED(p);
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief callback for custom events.
|
|
|
|
|
+ * @details this function is called when a custom event is sent to the view dispatcher.
|
|
|
|
|
+ * @param event the event id - ComboEventId value.
|
|
|
|
|
+ * @param context the context - ComboLockCrackerApp object.
|
|
|
|
|
+ */
|
|
|
|
|
+static bool combo_view_cracker_custom_event_callback(uint32_t event, void* context) {
|
|
|
|
|
+ ComboLockCrackerApp* app = (ComboLockCrackerApp*)context;
|
|
|
|
|
+
|
|
|
|
|
+ switch(event) {
|
|
|
|
|
+ case ComboEventIdRedrawScreen: {
|
|
|
|
|
+ bool redraw = true;
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker, ComboLockCrackerModel * _model, { UNUSED(_model); }, redraw);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ case ComboEventIdCalculateCombo: {
|
|
|
|
|
+ bool redraw = false;
|
|
|
|
|
+ with_view_model(
|
|
|
|
|
+ app->view_cracker,
|
|
|
|
|
+ ComboLockCrackerModel * model,
|
|
|
|
|
+ {
|
|
|
|
|
+ calculate_combo(model);
|
|
|
|
|
+ widget_reset(app->widget_results);
|
|
|
|
|
+ widget_add_text_scroll_element(app->widget_results, 0, 0, 128, 64, model->result);
|
|
|
|
|
+ },
|
|
|
|
|
+ redraw);
|
|
|
|
|
+
|
|
|
|
|
+ view_dispatcher_switch_to_view(app->view_dispatcher, ComboViewResults);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ default:
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- ComboCrackerState* app = malloc(sizeof(ComboCrackerState));
|
|
|
|
|
- app->first_lock = 0;
|
|
|
|
|
- app->second_lock = 0;
|
|
|
|
|
- app->resistance = 0.0f;
|
|
|
|
|
- app->selected = 0;
|
|
|
|
|
- app->exit = false;
|
|
|
|
|
- app->screen_state = SCREEN_COMBO_ENTRY;
|
|
|
|
|
- app->result[0] = '\0';
|
|
|
|
|
|
|
+static ComboLockCrackerApp* combo_app_alloc() {
|
|
|
|
|
+ ComboLockCrackerApp* app = (ComboLockCrackerApp*)malloc(sizeof(ComboLockCrackerApp));
|
|
|
|
|
+
|
|
|
|
|
+ Gui* gui = furi_record_open(RECORD_GUI);
|
|
|
|
|
+
|
|
|
|
|
+ app->view_dispatcher = view_dispatcher_alloc();
|
|
|
|
|
+ view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
|
|
|
|
+ view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
|
|
|
|
+
|
|
|
|
|
+ app->submenu = submenu_alloc();
|
|
|
|
|
+ submenu_add_item(
|
|
|
|
|
+ app->submenu, "Crack Lock", ComboSubmenuIndexCracker, combo_submenu_callback, app);
|
|
|
|
|
+ submenu_add_item(app->submenu, "About", ComboSubmenuIndexAbout, combo_submenu_callback, app);
|
|
|
|
|
+ view_set_previous_callback(submenu_get_view(app->submenu), combo_navigation_exit_callback);
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ app->view_dispatcher, ComboViewSubmenu, submenu_get_view(app->submenu));
|
|
|
|
|
+ view_dispatcher_switch_to_view(app->view_dispatcher, ComboViewSubmenu);
|
|
|
|
|
+
|
|
|
|
|
+ app->view_cracker = view_alloc();
|
|
|
|
|
+ view_set_draw_callback(app->view_cracker, combo_view_cracker_draw_callback);
|
|
|
|
|
+ view_set_input_callback(app->view_cracker, combo_view_cracker_input_callback);
|
|
|
|
|
+ view_set_previous_callback(app->view_cracker, combo_navigation_submenu_callback);
|
|
|
|
|
+ view_set_context(app->view_cracker, app);
|
|
|
|
|
+ view_set_custom_callback(app->view_cracker, combo_view_cracker_custom_event_callback);
|
|
|
|
|
+ view_allocate_model(app->view_cracker, ViewModelTypeLockFree, sizeof(ComboLockCrackerModel));
|
|
|
|
|
+
|
|
|
|
|
+ ComboLockCrackerModel* model = view_get_model(app->view_cracker);
|
|
|
|
|
+ model->first_lock = 0;
|
|
|
|
|
+ model->second_lock = 0;
|
|
|
|
|
+ model->resistance = 0.0f;
|
|
|
|
|
+ model->selected = 0;
|
|
|
|
|
+ model->result[0] = '\0';
|
|
|
|
|
+
|
|
|
|
|
+ view_dispatcher_add_view(app->view_dispatcher, ComboViewCracker, app->view_cracker);
|
|
|
|
|
+
|
|
|
|
|
+ app->widget_results = widget_alloc();
|
|
|
|
|
+ view_set_previous_callback(
|
|
|
|
|
+ widget_get_view(app->widget_results), combo_navigation_submenu_callback);
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ app->view_dispatcher, ComboViewResults, widget_get_view(app->widget_results));
|
|
|
|
|
+
|
|
|
|
|
+ app->widget_about = widget_alloc();
|
|
|
|
|
+ widget_add_text_scroll_element(
|
|
|
|
|
+ app->widget_about,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ 128,
|
|
|
|
|
+ 64,
|
|
|
|
|
+ "Combo Lock Cracker\n"
|
|
|
|
|
+ "---\n"
|
|
|
|
|
+ "Based on Samy Kamkar's Master Lock research.\n"
|
|
|
|
|
+ "Crack Combo Locks in 8 tries\n"
|
|
|
|
|
+ "https://samy.pl/master/\n"
|
|
|
|
|
+ "README at https://github.com/CharlesTheGreat77/ComboCracker-FZ\n");
|
|
|
|
|
+ view_set_previous_callback(
|
|
|
|
|
+ widget_get_view(app->widget_about), combo_navigation_submenu_callback);
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ app->view_dispatcher, ComboViewAbout, widget_get_view(app->widget_about));
|
|
|
|
|
+
|
|
|
|
|
+ app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
|
|
|
|
+
|
|
|
|
|
+ notification_message(app->notifications, &sequence_display_backlight_enforce_on);
|
|
|
|
|
+
|
|
|
|
|
+ return app;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- app->view_port = view_port_alloc();
|
|
|
|
|
- view_port_draw_callback_set(app->view_port, draw_callback, app);
|
|
|
|
|
- view_port_input_callback_set(app->view_port, input_callback, app);
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * @brief free the combo application.
|
|
|
|
|
+ * @details this function frees the application resources.
|
|
|
|
|
+ * @param app the application object.
|
|
|
|
|
+ */
|
|
|
|
|
+static void combo_app_free(ComboLockCrackerApp* app) {
|
|
|
|
|
+#ifdef BACKLIGHT_ON
|
|
|
|
|
+ notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
|
|
|
|
|
+#endif
|
|
|
|
|
+ furi_record_close(RECORD_NOTIFICATION);
|
|
|
|
|
+
|
|
|
|
|
+ view_dispatcher_remove_view(app->view_dispatcher, ComboViewAbout);
|
|
|
|
|
+ widget_free(app->widget_about);
|
|
|
|
|
+ view_dispatcher_remove_view(app->view_dispatcher, ComboViewResults);
|
|
|
|
|
+ widget_free(app->widget_results);
|
|
|
|
|
+ view_dispatcher_remove_view(app->view_dispatcher, ComboViewCracker);
|
|
|
|
|
+ view_free(app->view_cracker);
|
|
|
|
|
+ view_dispatcher_remove_view(app->view_dispatcher, ComboViewSubmenu);
|
|
|
|
|
+ submenu_free(app->submenu);
|
|
|
|
|
+ view_dispatcher_free(app->view_dispatcher);
|
|
|
|
|
+ furi_record_close(RECORD_GUI);
|
|
|
|
|
|
|
|
- app->gui = furi_record_open(RECORD_GUI);
|
|
|
|
|
- gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
|
|
|
|
|
|
+ free(app);
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- while(!app->exit) {
|
|
|
|
|
- furi_delay_ms(50);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+int32_t combo_cracker_app(void* _p) {
|
|
|
|
|
+ UNUSED(_p);
|
|
|
|
|
|
|
|
- view_port_enabled_set(app->view_port, false);
|
|
|
|
|
- gui_remove_view_port(app->gui, app->view_port);
|
|
|
|
|
- view_port_free(app->view_port);
|
|
|
|
|
- furi_record_close(RECORD_GUI);
|
|
|
|
|
- free(app);
|
|
|
|
|
|
|
+ ComboLockCrackerApp* app = combo_app_alloc();
|
|
|
|
|
+ view_dispatcher_run(app->view_dispatcher);
|
|
|
|
|
|
|
|
|
|
+ combo_app_free(app);
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|