Merge branch 'DarkFlippers:dev' into dev

This commit is contained in:
Gabriel Cirlig
2022-12-27 08:19:28 -05:00
committed by GitHub
301 changed files with 5090 additions and 1436 deletions
@@ -12,40 +12,88 @@ void init_types() {
upcA->name = "UPC-A";
upcA->numberOfDigits = 12;
upcA->startPos = 19;
upcA->bartype = BarTypeUPCA;
barcodeTypes[0] = upcA;
BarcodeType* ean8 = malloc(sizeof(BarcodeType));
ean8->name = "EAN-8";
ean8->numberOfDigits = 8;
ean8->startPos = 33;
ean8->bartype = BarTypeEAN8;
barcodeTypes[1] = ean8;
BarcodeType* ean13 = malloc(sizeof(BarcodeType));
ean13->name = "EAN-13";
ean13->numberOfDigits = 13;
ean13->startPos = 19;
ean13->bartype = BarTypeEAN13;
barcodeTypes[2] = ean13;
}
void draw_digit(Canvas* canvas, int digit, bool rightHand, int startingPosition) {
void draw_digit(
Canvas* canvas,
int digit,
BarEncodingType rightHand,
int startingPosition,
bool drawlines) {
char digitStr[2];
snprintf(digitStr, 2, "%u", digit);
canvas_set_color(canvas, ColorBlack);
canvas_draw_str(
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, digitStr);
if(rightHand) {
canvas_set_color(canvas, ColorBlack);
} else {
canvas_set_color(canvas, ColorWhite);
}
int count = 0;
for(int i = 0; i < 4; i++) {
canvas_draw_box(
canvas, startingPosition + count, BARCODE_Y_START, DIGITS[digit][i], BARCODE_HEIGHT);
canvas_invert_color(canvas);
count += DIGITS[digit][i];
if(drawlines) {
switch(rightHand) {
case BarEncodingTypeLeft:
case BarEncodingTypeRight:
canvas_set_color(
canvas, (rightHand == BarEncodingTypeRight) ? ColorBlack : ColorWhite);
//int count = 0;
for(int i = 0; i < 4; i++) {
canvas_draw_box(
canvas, startingPosition, BARCODE_Y_START, DIGITS[digit][i], BARCODE_HEIGHT);
canvas_invert_color(canvas);
startingPosition += DIGITS[digit][i];
}
break;
case BarEncodingTypeG:
canvas_set_color(canvas, ColorWhite);
//int count = 0;
for(int i = 3; i >= 0; i--) {
canvas_draw_box(
canvas, startingPosition, BARCODE_Y_START, DIGITS[digit][i], BARCODE_HEIGHT);
canvas_invert_color(canvas);
startingPosition += DIGITS[digit][i];
}
break;
default:
break;
}
}
}
int get_digit_position(int index, BarcodeType* type) {
int pos = type->startPos + index * 7;
if(index >= type->numberOfDigits / 2) {
pos += 5;
int pos = 0;
switch(type->bartype) {
case BarTypeEAN8:
case BarTypeUPCA:
pos = type->startPos + index * 7;
if(index >= type->numberOfDigits / 2) {
pos += 5;
}
break;
case BarTypeEAN13:
if(index == 0)
pos = type->startPos - 10;
else {
pos = type->startPos + (index - 1) * 7;
if((index - 1) >= type->numberOfDigits / 2) {
pos += 5;
}
}
break;
default:
break;
}
return pos;
}
@@ -54,18 +102,30 @@ int get_menu_text_location(int index) {
return 20 + 10 * index;
}
int get_barcode_max_index(PluginState* plugin_state) {
return plugin_state->doParityCalculation ?
barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 :
barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits;
}
int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
int checkDigit = 0;
int checkDigitOdd = 0;
int checkDigitEven = 0;
//add all odd positions. Confusing because 0index
for(int i = 0; i < type->numberOfDigits - 1; i += 2) {
checkDigit += plugin_state->barcodeNumeral[i];
checkDigitOdd += plugin_state->barcodeNumeral[i];
}
checkDigit = checkDigit * 3; //times 3
//add all even positions to above. Confusing because 0index
for(int i = 1; i < type->numberOfDigits - 1; i += 2) {
checkDigit += plugin_state->barcodeNumeral[i];
checkDigitEven += plugin_state->barcodeNumeral[i];
}
if(type->bartype == BarTypeEAN13) {
checkDigit = checkDigitEven * 3 + checkDigitOdd;
} else {
checkDigit = checkDigitOdd * 3 + checkDigitEven;
}
checkDigit = checkDigit % 10; //mod 10
@@ -103,34 +163,57 @@ static void render_callback(Canvas* const canvas, void* ctx) {
AlignCenter,
(barcodeTypes[plugin_state->barcodeTypeIndex])->name);
canvas_draw_disc(
canvas, 40, get_menu_text_location(plugin_state->menuIndex) - 1, 2); //draw menu cursor
canvas,
40,
get_menu_text_location(plugin_state->menuIndex) - 1,
2); //draw menu cursor
} else {
BarcodeType* type = barcodeTypes[plugin_state->barcodeTypeIndex];
//start saftey
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, type->startPos - 3, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
canvas_draw_box(
canvas,
(type->startPos - 1),
BARCODE_Y_START,
1,
BARCODE_HEIGHT + 2); //start saftey
canvas_draw_box(canvas, (type->startPos - 1), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
for(int index = 0; index < type->numberOfDigits; index++) {
bool isOnRight = false;
if(index >= type->numberOfDigits / 2) {
isOnRight = true;
}
if((index == type->numberOfDigits - 1) &&
(plugin_state->doParityCalculation)) { //calculate the check digit
int checkDigit = calculate_check_digit(plugin_state, type);
plugin_state->barcodeNumeral[type->numberOfDigits - 1] = checkDigit;
int startpos = 0;
int endpos = type->numberOfDigits;
if(type->bartype == BarTypeEAN13) {
startpos++;
draw_digit(
canvas,
plugin_state->barcodeNumeral[0],
BarEncodingTypeRight,
get_digit_position(0, barcodeTypes[plugin_state->barcodeTypeIndex]),
false);
}
if(plugin_state->doParityCalculation) { //calculate the check digit
plugin_state->barcodeNumeral[type->numberOfDigits - 1] =
calculate_check_digit(plugin_state, type);
}
for(int index = startpos; index < endpos; index++) {
BarEncodingType barEncodingType = BarEncodingTypeLeft;
if(type->bartype == BarTypeEAN13) {
if(index - 1 >= (type->numberOfDigits - 1) / 2) {
barEncodingType = BarEncodingTypeRight;
} else {
barEncodingType =
(FURI_BIT(EAN13ENCODE[plugin_state->barcodeNumeral[0]], index - 1)) ?
BarEncodingTypeG :
BarEncodingTypeLeft;
}
} else {
if(index >= type->numberOfDigits / 2) {
barEncodingType = BarEncodingTypeRight;
}
}
int digitPosition =
get_digit_position(index, barcodeTypes[plugin_state->barcodeTypeIndex]);
draw_digit(canvas, plugin_state->barcodeNumeral[index], isOnRight, digitPosition);
draw_digit(
canvas, plugin_state->barcodeNumeral[index], barEncodingType, digitPosition, true);
}
//central separator
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 62, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
canvas_draw_box(canvas, 64, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
@@ -147,15 +230,11 @@ static void render_callback(Canvas* const canvas, void* ctx) {
1); //draw editing cursor
}
//end safety
int endSafetyPosition = get_digit_position(type->numberOfDigits - 1, type) + 7;
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, endSafetyPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
canvas_draw_box(
canvas,
(endSafetyPosition + 2),
BARCODE_Y_START,
1,
BARCODE_HEIGHT + 2); //end safety
canvas_draw_box(canvas, (endSafetyPosition + 2), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
}
release_mutex((ValueMutex*)ctx, plugin_state);
@@ -169,7 +248,7 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
}
static void barcode_generator_state_init(PluginState* const plugin_state) {
for(int i = 0; i < 12; ++i) {
for(int i = 0; i < BARCODE_MAX_LENS; ++i) {
plugin_state->barcodeNumeral[i] = i % 10;
}
plugin_state->editingIndex = 0;
@@ -194,9 +273,7 @@ static bool handle_key_press_view(InputKey key, PluginState* plugin_state) {
}
static bool handle_key_press_edit(InputKey key, PluginState* plugin_state) {
int barcodeMaxIndex = plugin_state->doParityCalculation ?
barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 :
barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits;
int barcodeMaxIndex = get_barcode_max_index(plugin_state);
switch(key) {
case InputKeyUp:
@@ -286,6 +363,9 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) {
default:
break;
}
int barcodeMaxIndex = get_barcode_max_index(plugin_state);
if(plugin_state->editingIndex >= barcodeMaxIndex)
plugin_state->editingIndex = barcodeMaxIndex - 1;
return true;
}
@@ -1,7 +1,8 @@
#define BARCODE_HEIGHT 50
#define BARCODE_Y_START 3
#define BARCODE_TEXT_OFFSET 9
#define NUMBER_OF_BARCODE_TYPES 2
#define BARCODE_MAX_LENS 13
#define NUMBER_OF_BARCODE_TYPES 3
#define MENU_INDEX_VIEW 0
#define MENU_INDEX_EDIT 1
#define MENU_INDEX_PARITY 2
@@ -23,14 +24,27 @@ typedef enum {
MenuMode,
} Mode;
typedef enum {
BarEncodingTypeLeft,
BarEncodingTypeRight,
BarEncodingTypeG,
} BarEncodingType;
typedef enum {
BarTypeEAN8,
BarTypeUPCA,
BarTypeEAN13,
} BarType;
typedef struct {
char* name;
int numberOfDigits;
int startPos;
BarType bartype;
} BarcodeType;
typedef struct {
int barcodeNumeral[12]; //The current barcode number
int barcodeNumeral[BARCODE_MAX_LENS]; //The current barcode number
int editingIndex; //The index of the editing symbol
int menuIndex; //The index of the menu cursor
Mode mode; //View, edit or menu
@@ -50,3 +64,16 @@ static const int DIGITS[10][4] = {
{1, 2, 1, 3},
{3, 1, 1, 2},
};
static const uint8_t EAN13ENCODE[10] = {
0b000000,
0b110100,
0b101100,
0b011100,
0b110010,
0b100110,
0b001110,
0b101010,
0b011010,
0b010110,
};
@@ -568,12 +568,12 @@ void dap_common_usb_set_state_callback(DapStateCallback callback) {
static void* dap_usb_alloc_string_descr(const char* str) {
furi_assert(str);
uint8_t len = strlen(str);
uint8_t wlen = (len + 1) * sizeof(uint16_t);
size_t len = strlen(str);
size_t wlen = (len + 1) * sizeof(uint16_t);
struct usb_string_descriptor* dev_str_desc = malloc(wlen);
dev_str_desc->bLength = wlen;
dev_str_desc->bDescriptorType = USB_DTYPE_STRING;
for(uint8_t i = 0; i < len; i++) {
for(size_t i = 0; i < len; i++) {
dev_str_desc->wString[i] = str[i];
}
@@ -974,4 +974,4 @@ static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal
}
return usbd_fail;
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Eugene Kirzhanov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+17
View File
@@ -0,0 +1,17 @@
# "2048" game for Flipper Zero
- play up to 65K
- progress is saved on exit
![Game screen](images/screenshot1.png)
![Menu screen](images/screenshot2.png)
#### TODO:
- add animations
#### Thanks to:
- [DroomOne's FlappyBird](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
- [x27's "15" Game](https://github.com/x27/flipperzero-game15)
#### License
[MIT](LICENSE)
Copyright 2022 Eugene Kirzhanov
@@ -0,0 +1,14 @@
App(
appid="game_2048",
name="2048",
apptype=FlipperAppType.EXTERNAL,
entry_point="game_2048_app",
cdefines=["APP_GAME_2048"],
requires=[
"gui",
],
stack_size=1 * 1024,
order=90,
fap_icon="game_2048.png",
fap_category="Games"
)
@@ -0,0 +1,40 @@
#include "array_utils.h"
void reverse_array(int length, uint8_t arr[length]) {
uint8_t tmp;
for(int low = 0, high = length - 1; low < high; low++, high--) {
tmp = arr[low];
arr[low] = arr[high];
arr[high] = tmp;
}
}
bool shift_array_to_left(int length, uint8_t arr[length], uint8_t from_index, uint8_t offset) {
if(from_index >= length) return false;
for(uint8_t i = from_index; i < length; i++) {
arr[i] = i < length - offset ? arr[i + offset] : 0;
}
return true;
}
void get_column_from_array(
int rows,
int cols,
uint8_t arr[rows][cols],
uint8_t column_index,
uint8_t* out) {
for(uint8_t i = 0; i < rows; i++) {
out[i] = arr[i][column_index];
}
}
void set_column_to_array(
int rows,
int cols,
uint8_t arr[rows][cols],
uint8_t column_index,
uint8_t* src) {
for(uint8_t i = 0; i < rows; i++) {
arr[i][column_index] = src[i];
}
}
@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
void reverse_array(int length, uint8_t arr[length]);
bool shift_array_to_left(int length, uint8_t arr[length], uint8_t from_index, uint8_t offset);
void get_column_from_array(
int rows,
int cols,
uint8_t arr[rows][cols],
uint8_t column_index,
uint8_t* out);
void set_column_to_array(
int rows,
int cols,
uint8_t arr[rows][cols],
uint8_t column_index,
uint8_t* src);
+263
View File
@@ -0,0 +1,263 @@
#pragma once
#include <stdint.h>
uint8_t digits[16][14][14] = {
// 2
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 4
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 8
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 16
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 32
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 64
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 128
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 256
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 512
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 1K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 2K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 4K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 8K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 16K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0},
{0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 32K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0},
{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0},
{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
// 64K
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},
{0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0},
{0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0},
{0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}
};
+509
View File
@@ -0,0 +1,509 @@
/*
* Copyright 2022 Eugene Kirzhanov
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*
* Thanks to:
* - DroomOne: https://github.com/DroomOne/flipperzero-firmware
* - x27: https://github.com/x27/flipperzero-game15
*/
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <storage/storage.h>
#include "digits.h"
#include "array_utils.h"
#define CELLS_COUNT 4
#define CELL_INNER_SIZE 14
#define FRAME_LEFT 10
#define FRAME_TOP 1
#define FRAME_SIZE 61
#define SAVING_DIRECTORY "/ext/apps/Games"
#define SAVING_FILENAME SAVING_DIRECTORY "/game_2048.save"
typedef enum {
GameStateMenu,
GameStateInProgress,
GameStateGameOver,
} State;
typedef struct {
State state;
uint8_t table[CELLS_COUNT][CELLS_COUNT];
uint32_t score;
uint32_t moves;
int8_t selected_menu_item;
uint32_t top_score;
} GameState;
typedef struct {
uint32_t points;
bool is_table_updated;
} MoveResult;
#define MENU_ITEMS_COUNT 2
static const char* popup_menu_strings[] = {"Resume", "New Game"};
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
static void draw_frame(Canvas* canvas) {
canvas_draw_frame(canvas, FRAME_LEFT, FRAME_TOP, FRAME_SIZE, FRAME_SIZE);
uint8_t offs = FRAME_LEFT + CELL_INNER_SIZE + 1;
for(uint8_t i = 0; i < CELLS_COUNT - 1; i++) {
canvas_draw_line(canvas, offs, FRAME_TOP + 1, offs, FRAME_TOP + FRAME_SIZE - 2);
offs += CELL_INNER_SIZE + 1;
}
offs = FRAME_TOP + CELL_INNER_SIZE + 1;
for(uint8_t i = 0; i < CELLS_COUNT - 1; i++) {
canvas_draw_line(canvas, FRAME_LEFT + 1, offs, FRAME_LEFT + FRAME_SIZE - 2, offs);
offs += CELL_INNER_SIZE + 1;
}
}
static void draw_digit(Canvas* canvas, uint8_t row, uint8_t column, uint8_t value) {
if(value == 0) return;
uint8_t left = FRAME_LEFT + 1 + (column * (CELL_INNER_SIZE + 1));
uint8_t top = FRAME_TOP + 1 + (row * (CELL_INNER_SIZE + 1));
for(uint8_t r = 0; r < CELL_INNER_SIZE; r++) {
for(u_int8_t c = 0; c < CELL_INNER_SIZE; c++) {
if(digits[value - 1][r][c] == 1) {
canvas_draw_dot(canvas, left + c, top + r);
}
}
}
}
static void draw_table(Canvas* canvas, const uint8_t table[CELLS_COUNT][CELLS_COUNT]) {
for(uint8_t row = 0; row < CELLS_COUNT; row++) {
for(uint8_t column = 0; column < CELLS_COUNT; column++) {
draw_digit(canvas, row, column, table[row][column]);
}
}
}
static void gray_canvas(Canvas* const canvas) {
canvas_set_color(canvas, ColorWhite);
for(int x = 0; x < 128; x += 2) {
for(int y = 0; y < 64; y++) {
canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y);
}
}
}
static void draw_callback(Canvas* const canvas, void* ctx) {
const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25);
if(game_state == NULL) return;
canvas_clear(canvas);
draw_frame(canvas);
draw_table(canvas, game_state->table);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 128, FRAME_TOP, AlignRight, AlignTop, "Score");
canvas_draw_str_aligned(canvas, 128, FRAME_TOP + 20, AlignRight, AlignTop, "Moves");
canvas_draw_str_aligned(canvas, 128, FRAME_TOP + 40, AlignRight, AlignTop, "Top Score");
int bufSize = 12;
char buf[bufSize];
snprintf(buf, sizeof(buf), "%lu", game_state->score);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 128, FRAME_TOP + 10, AlignRight, AlignTop, buf);
memset(buf, 0, bufSize);
snprintf(buf, sizeof(buf), "%lu", game_state->moves);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 128, FRAME_TOP + 30, AlignRight, AlignTop, buf);
memset(buf, 0, bufSize);
snprintf(buf, sizeof(buf), "%lu", game_state->top_score);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 128, FRAME_TOP + 50, AlignRight, AlignTop, buf);
if(game_state->state == GameStateMenu) {
gray_canvas(canvas);
canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, 28, 16, 72, 32, 4);
canvas_set_color(canvas, ColorBlack);
canvas_draw_rframe(canvas, 28, 16, 72, 32, 4);
for(int i = 0; i < MENU_ITEMS_COUNT; i++) {
if(i == game_state->selected_menu_item) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12);
}
canvas_set_color(
canvas, i == game_state->selected_menu_item ? ColorWhite : ColorBlack);
canvas_draw_str_aligned(
canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]);
}
} else if(game_state->state == GameStateGameOver) {
gray_canvas(canvas);
bool record_broken = game_state->score > game_state->top_score;
canvas_set_color(canvas, ColorWhite);
canvas_draw_rbox(canvas, 14, 12, 100, 40, 4);
canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, 14, 26, 114, 26);
canvas_draw_rframe(canvas, 14, 12, 100, 40, 4);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Game Over");
canvas_set_font(canvas, FontSecondary);
if(record_broken) {
canvas_draw_str_aligned(canvas, 64, 29, AlignCenter, AlignTop, "New Top Score!!!");
} else {
canvas_draw_str_aligned(canvas, 64, 29, AlignCenter, AlignTop, "Your Score");
}
memset(buf, 0, bufSize);
snprintf(buf, sizeof(buf), "%lu", game_state->score);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 48, AlignCenter, AlignBottom, buf);
}
release_mutex((ValueMutex*)ctx, game_state);
}
void calculate_move_to_left(uint8_t arr[], MoveResult* const move_result) {
uint8_t index = 0;
uint8_t next_index;
uint8_t offset;
bool was_moved;
while(index < CELLS_COUNT - 1) {
// find offset from [index] to next non-empty value
offset = 1;
while(index + offset < CELLS_COUNT && arr[index + offset] == 0) offset++;
// if all remaining values in this row are empty then go to next row
if(index + offset >= CELLS_COUNT) break;
// if current cell is empty then shift all cells [index+offset .. CELLS_COUNT-1] to [index]
if(arr[index] == 0) {
was_moved = shift_array_to_left(CELLS_COUNT, arr, index, offset);
if(was_moved) move_result->is_table_updated = true;
}
next_index = index + 1;
if(arr[next_index] == 0) {
// find offset from [next_index] to next non-empty value
offset = 1;
while(next_index + offset < CELLS_COUNT && arr[next_index + offset] == 0) offset++;
// if all remaining values in this row are empty then go to next row
if(next_index + offset >= CELLS_COUNT) break;
// if next cell is empty then shift cells [next_index+offset .. CELLS_COUNT-1] to [next_index]
was_moved = shift_array_to_left(CELLS_COUNT, arr, next_index, offset);
if(was_moved) move_result->is_table_updated = true;
}
if(arr[index] == arr[next_index]) {
arr[index]++;
shift_array_to_left(CELLS_COUNT, arr, next_index, 1);
move_result->is_table_updated = true;
move_result->points += 2 << (arr[index] - 1);
}
index++;
}
}
void move_left(uint8_t table[CELLS_COUNT][CELLS_COUNT], MoveResult* const move_result) {
for(uint8_t row_index = 0; row_index < CELLS_COUNT; row_index++) {
calculate_move_to_left(table[row_index], move_result);
}
}
void move_right(uint8_t table[CELLS_COUNT][CELLS_COUNT], MoveResult* const move_result) {
for(uint8_t row_index = 0; row_index < CELLS_COUNT; row_index++) {
reverse_array(CELLS_COUNT, table[row_index]);
calculate_move_to_left(table[row_index], move_result);
reverse_array(CELLS_COUNT, table[row_index]);
}
}
void move_up(uint8_t table[CELLS_COUNT][CELLS_COUNT], MoveResult* const move_result) {
uint8_t column[CELLS_COUNT];
for(uint8_t column_index = 0; column_index < CELLS_COUNT; column_index++) {
get_column_from_array(CELLS_COUNT, CELLS_COUNT, table, column_index, column);
calculate_move_to_left(column, move_result);
set_column_to_array(CELLS_COUNT, CELLS_COUNT, table, column_index, column);
}
}
void move_down(uint8_t table[CELLS_COUNT][CELLS_COUNT], MoveResult* const move_result) {
uint8_t column[CELLS_COUNT];
for(uint8_t column_index = 0; column_index < CELLS_COUNT; column_index++) {
get_column_from_array(CELLS_COUNT, CELLS_COUNT, table, column_index, column);
reverse_array(CELLS_COUNT, column);
calculate_move_to_left(column, move_result);
reverse_array(CELLS_COUNT, column);
set_column_to_array(CELLS_COUNT, CELLS_COUNT, table, column_index, column);
}
}
void add_new_digit(GameState* const game_state) {
uint8_t empty_cell_indexes[CELLS_COUNT * CELLS_COUNT];
uint8_t empty_cells_count = 0;
for(u_int8_t i = 0; i < CELLS_COUNT; i++) {
for(u_int8_t j = 0; j < CELLS_COUNT; j++) {
if(game_state->table[i][j] == 0) {
empty_cell_indexes[empty_cells_count++] = i * CELLS_COUNT + j;
}
}
}
if(empty_cells_count == 0) return;
int random_empty_cell_index = empty_cell_indexes[random() % empty_cells_count];
u_int8_t row = random_empty_cell_index / CELLS_COUNT;
u_int8_t col = random_empty_cell_index % CELLS_COUNT;
int random_value_percent = random() % 100;
game_state->table[row][col] = random_value_percent < 90 ? 1 : 2; // 90% for 2, 25% for 4
}
void init_game(GameState* const game_state, bool clear_top_score) {
memset(game_state->table, 0, CELLS_COUNT * CELLS_COUNT * sizeof(uint8_t));
add_new_digit(game_state);
add_new_digit(game_state);
game_state->score = 0;
game_state->moves = 0;
game_state->state = GameStateInProgress;
game_state->selected_menu_item = 0;
if(clear_top_score) {
game_state->top_score = 0;
}
}
bool load_game(GameState* game_state) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
uint16_t bytes_readed = 0;
if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) {
bytes_readed = storage_file_read(file, game_state, sizeof(GameState));
}
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
return bytes_readed == sizeof(GameState);
}
void save_game(GameState* game_state) {
Storage* storage = furi_record_open(RECORD_STORAGE);
if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) {
if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) {
return;
}
}
File* file = storage_file_alloc(storage);
if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
storage_file_write(file, game_state, sizeof(GameState));
}
storage_file_close(file);
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
bool is_game_over(GameState* const game_state) {
FURI_LOG_I("is_game_over", "====check====");
// check if table contains at least one empty cell
for(uint8_t i = 0; i < CELLS_COUNT; i++) {
for(u_int8_t j = 0; j < CELLS_COUNT; j++) {
if(game_state->table[i][j] == 0) {
FURI_LOG_I("is_game_over", "has empty cells");
return false;
}
}
}
FURI_LOG_I("is_game_over", "no empty cells");
uint8_t tmp_table[CELLS_COUNT][CELLS_COUNT];
MoveResult* tmp_move_result = malloc(sizeof(MoveResult));
// check if we can move to any direction
memcpy(tmp_table, game_state->table, CELLS_COUNT * CELLS_COUNT * sizeof(uint8_t));
move_left(tmp_table, tmp_move_result);
if(tmp_move_result->is_table_updated) return false;
FURI_LOG_I("is_game_over", "can't move left");
memcpy(tmp_table, game_state->table, CELLS_COUNT * CELLS_COUNT * sizeof(uint8_t));
move_right(tmp_table, tmp_move_result);
if(tmp_move_result->is_table_updated) return false;
FURI_LOG_I("is_game_over", "can't move right");
memcpy(tmp_table, game_state->table, CELLS_COUNT * CELLS_COUNT * sizeof(uint8_t));
move_up(tmp_table, tmp_move_result);
if(tmp_move_result->is_table_updated) return false;
FURI_LOG_I("is_game_over", "can't move up");
memcpy(tmp_table, game_state->table, CELLS_COUNT * CELLS_COUNT * sizeof(uint8_t));
move_down(tmp_table, tmp_move_result);
if(tmp_move_result->is_table_updated) return false;
FURI_LOG_I("is_game_over", "can't move down");
return true;
}
int32_t game_2048_app() {
GameState* game_state = malloc(sizeof(GameState));
if(!load_game(game_state)) {
init_game(game_state, true);
}
MoveResult* move_result = malloc(sizeof(MoveResult));
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
free(game_state);
return 255;
}
InputEvent input;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
bool is_finished = false;
while(!is_finished) {
FuriStatus event_status = furi_message_queue_get(event_queue, &input, FuriWaitForever);
if(event_status == FuriStatusOk) {
// handle only press event, ignore repeat/release events
if(input.type != InputTypePress) continue;
GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex);
switch(game_state->state) {
case GameStateMenu:
switch(input.key) {
case InputKeyUp:
game_state->selected_menu_item--;
if(game_state->selected_menu_item < 0) {
game_state->selected_menu_item = MENU_ITEMS_COUNT - 1;
}
break;
case InputKeyDown:
game_state->selected_menu_item++;
if(game_state->selected_menu_item >= MENU_ITEMS_COUNT) {
game_state->selected_menu_item = 0;
}
break;
case InputKeyOk:
if(game_state->selected_menu_item == 1) {
// new game
init_game(game_state, false);
save_game(game_state);
}
game_state->state = GameStateInProgress;
break;
case InputKeyBack:
game_state->state = GameStateInProgress;
break;
default:
break;
}
break;
case GameStateInProgress:
move_result->is_table_updated = false;
move_result->points = 0;
switch(input.key) {
case InputKeyLeft:
move_left(game_state->table, move_result);
break;
case InputKeyRight:
move_right(game_state->table, move_result);
break;
case InputKeyUp:
move_up(game_state->table, move_result);
break;
case InputKeyDown:
move_down(game_state->table, move_result);
break;
case InputKeyOk:
game_state->state = GameStateMenu;
game_state->selected_menu_item = 0;
break;
case InputKeyBack:
save_game(game_state);
is_finished = true;
break;
case InputKeyMAX:
break;
}
game_state->score += move_result->points;
if(move_result->is_table_updated) {
game_state->moves++;
add_new_digit(game_state);
}
if(is_game_over(game_state)) {
game_state->state = GameStateGameOver;
if(game_state->score >= game_state->top_score) {
game_state->top_score = game_state->score;
}
}
break;
case GameStateGameOver:
if(input.key == InputKeyOk || input.key == InputKeyBack) {
init_game(game_state, false);
save_game(game_state);
}
}
view_port_update(view_port);
release_mutex(&state_mutex, game_state);
}
}
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
free(game_state);
free(move_result);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 657 B

+9 -5
View File
@@ -42,10 +42,12 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
furi_assert(context);
Hid* hid = context;
bool connected = (status == BtStatusConnected);
if(connected) {
notification_internal_message(hid->notifications, &sequence_set_blue_255);
} else {
notification_internal_message(hid->notifications, &sequence_reset_blue);
if(hid->transport == HidTransportBle) {
if(connected) {
notification_internal_message(hid->notifications, &sequence_set_blue_255);
} else {
notification_internal_message(hid->notifications, &sequence_reset_blue);
}
}
hid_keynote_set_connected_status(hid->hid_keynote, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
@@ -186,7 +188,9 @@ void hid_free(Hid* app) {
furi_assert(app);
// Reset notification
notification_internal_message(app->notifications, &sequence_reset_blue);
if(app->transport == HidTransportBle) {
notification_internal_message(app->notifications, &sequence_reset_blue);
}
// Free views
view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
@@ -25,6 +25,7 @@ typedef struct {
bool back_pressed;
bool connected;
char key_string[5];
HidTransport transport;
} HidKeyboardModel;
typedef struct {
@@ -207,7 +208,7 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
HidKeyboardModel* model = context;
// Header
if(!model->connected) {
if((!model->connected) && (model->transport == HidTransportBle)) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard");
@@ -249,30 +250,19 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) {
HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x];
// Use upper case if shift is toggled
bool useUppercase = model->shift;
// Check if the key has an upper case version
bool hasUppercase = key.shift_key != 0;
if(useUppercase && hasUppercase)
return key.value;
else
return key.value;
return key.value;
}
static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) {
// Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
do {
if(((int8_t)model->y) + delta.y < 0)
model->y = ROW_COUNT - 1;
else
model->y = (model->y + delta.y) % ROW_COUNT;
const int delta_sum = model->y + delta.y;
model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT;
} while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0);
do {
if(((int8_t)model->x) + delta.x < 0)
model->x = COLUMN_COUNT - 1;
else
model->x = (model->x + delta.x) % COLUMN_COUNT;
const int delta_sum = model->x + delta.x;
model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT;
} while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width ==
0); // Skip zero width keys, pretend they are one key
}
@@ -372,6 +362,12 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback);
view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);
with_view_model(
hid_keyboard->view,
HidKeyboardModel * model,
{ model->transport = bt_hid->transport; },
true);
return hid_keyboard;
}
@@ -19,6 +19,7 @@ typedef struct {
bool ok_pressed;
bool back_pressed;
bool connected;
HidTransport transport;
} HidKeynoteModel;
static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -39,11 +40,14 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
HidKeynoteModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
@@ -186,6 +190,9 @@ HidKeynote* hid_keynote_alloc(Hid* hid) {
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);
with_view_model(
hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true);
return hid_keynote;
}
+11 -4
View File
@@ -21,6 +21,7 @@ typedef struct {
bool down_pressed;
bool ok_pressed;
bool connected;
HidTransport transport;
} HidMediaModel;
static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -41,11 +42,14 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
HidMediaModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media");
canvas_set_font(canvas, FontSecondary);
@@ -190,6 +194,9 @@ HidMedia* hid_media_alloc(Hid* hid) {
view_set_draw_callback(hid_media->view, hid_media_draw_callback);
view_set_input_callback(hid_media->view, hid_media_input_callback);
with_view_model(
hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true);
return hid_media;
}
+11 -4
View File
@@ -20,6 +20,7 @@ typedef struct {
bool left_mouse_held;
bool right_mouse_pressed;
bool connected;
HidTransport transport;
} HidMouseModel;
static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
@@ -27,11 +28,14 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
HidMouseModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse");
canvas_set_font(canvas, FontSecondary);
@@ -198,6 +202,9 @@ HidMouse* hid_mouse_alloc(Hid* hid) {
view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback);
view_set_input_callback(hid_mouse->view, hid_mouse_input_callback);
with_view_model(
hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true);
return hid_mouse;
}
@@ -16,6 +16,7 @@ typedef struct {
bool connected;
bool running;
uint8_t counter;
HidTransport transport;
} HidMouseJigglerModel;
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
@@ -23,11 +24,14 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
HidMouseJigglerModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
@@ -120,6 +124,12 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
hid_mouse_jiggler->timer = furi_timer_alloc(
hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{ model->transport = hid->transport; },
true);
return hid_mouse_jiggler;
}
@@ -19,6 +19,7 @@ typedef struct {
bool ok_pressed;
bool connected;
bool is_cursor_set;
HidTransport transport;
} HidTikTokModel;
static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
@@ -26,11 +27,14 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
HidTikTokModel* model = context;
// Header
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
canvas_set_font(canvas, FontSecondary);
@@ -207,6 +211,9 @@ HidTikTok* hid_tiktok_alloc(Hid* bt_hid) {
view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback);
view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback);
with_view_model(
hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true);
return hid_tiktok;
}
@@ -180,7 +180,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
// note stack view_port
x_pos = 73;
y_pos = 0;
y_pos = 0; //-V1048
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
canvas_draw_frame(canvas, x_pos, y_pos, 49, 64);
@@ -111,9 +111,9 @@ static void init_opt_select_LUT(void) {
***********************************************************************************/
#define loclass_opt__select(x, y, r) \
(4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ((r | r << 2) >> 3))) | \
(2 & (((r | r << 2) >> 6) ^ ((r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x ^ y) << 1))) | \
(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x))
(4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) | \
(2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \
(1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x)))
static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
uint16_t Tt = s->t & 0xc533;
@@ -149,30 +149,11 @@ static void loclass_opt_suc(
uint8_t length,
bool add32Zeroes) {
for(int i = 0; i < length; i++) {
uint8_t head;
head = in[i];
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
uint8_t head = in[i];
for(int j = 0; j < 8; j++) {
loclass_opt_successor(k, s, head);
head >>= 1;
}
}
//For tag MAC, an additional 32 zeroes
if(add32Zeroes) {
@@ -143,7 +143,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]);
rfalPicoPassReadBlockRes cfg = {0};
err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg);
memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data));
FURI_LOG_D(
TAG,
@@ -158,7 +158,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]);
rfalPicoPassReadBlockRes aia;
err = rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia);
rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia);
memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data));
FURI_LOG_D(
TAG,
@@ -221,7 +221,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
while(iclass_elite_dict_get_next_key(dict, key)) {
FURI_LOG_D(
TAG,
"Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x",
"Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
index++,
key[0],
key[1],
@@ -249,9 +249,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
}
}
if(dict) {
iclass_elite_dict_free(dict);
}
iclass_elite_dict_free(dict);
return err;
}
@@ -54,7 +54,7 @@ bool picopass_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventTextInputDone) {
if(strcmp(picopass->dev->dev_name, "")) {
if(strcmp(picopass->dev->dev_name, "") != 0) {
// picopass_device_delete(picopass->dev, true);
}
strlcpy(
@@ -307,7 +307,7 @@ bool pcsg_view_receiver_input(InputEvent* event, void* context) {
pcsg_receiver->view,
PCSGReceiverModel * model,
{
if(model->idx != model->history_item - 1) model->idx++;
if(model->history_item && model->idx != model->history_item - 1) model->idx++;
},
true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
@@ -15,12 +15,12 @@ static void
app->pwm_freq = freq;
app->pwm_duty = duty;
if(app->pwm_ch != pwm_ch_id[channel_id]) {
if(app->pwm_ch != pwm_ch_id[channel_id]) { //-V1051
app->pwm_ch_prev = app->pwm_ch;
app->pwm_ch = pwm_ch_id[channel_id];
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventChannelChange);
} else {
app->pwm_ch = pwm_ch_id[channel_id];
app->pwm_ch = pwm_ch_id[channel_id]; //-V1048
view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventUpdate);
}
}
@@ -127,12 +127,12 @@ static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) {
char* line_label = NULL;
char val_text[16];
for(uint8_t line = 0; line < LineIndexTotalCount; line++) {
for(size_t line = 0; line < LineIndexTotalCount; line++) {
if(line == LineIndexChannel) {
line_label = "GPIO Pin";
} else if(line == LineIndexFrequency) {
line_label = "Frequency";
} else if(line == LineIndexDuty) {
} else if(line == LineIndexDuty) { //-V547
line_label = "Pulse width";
}
@@ -169,7 +169,7 @@ static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, icon_x, text_y - 9, &I_SmallArrowUp_3x5);
canvas_draw_icon(canvas, icon_x, text_y + 5, &I_SmallArrowDown_3x5);
}
} else if(line == LineIndexDuty) {
} else if(line == LineIndexDuty) { //-V547
snprintf(val_text, sizeof(val_text), "%d%%", model->duty);
canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text);
if(model->duty != 0) {
+2 -2
View File
@@ -130,7 +130,7 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
canvas_set_font(canvas, FontSecondary);
char buffer[12];
snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7);
snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U);
canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);
}
@@ -153,7 +153,7 @@ static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) {
static void snake_game_init_game(SnakeState* const snake_state) {
Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}};
memcpy(snake_state->points, p, sizeof(p));
memcpy(snake_state->points, p, sizeof(p)); //-V1086
snake_state->len = 7;
@@ -38,6 +38,9 @@
TOTP_CLI_PRINTF( \
"Invalid command arguments. use \"help\" command to get list of available commands")
#define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \
TOTP_CLI_PRINTF("An error has occurred during updating config file\r\n")
/**
* @brief Checks whether user is authenticated and entered correct PIN.
* If user is not authenticated it prompts user to enter correct PIN to authenticate.
@@ -206,11 +206,13 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check);
plugin_state->tokens_count++;
totp_config_file_save_new_token(token_info);
if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF("Token \"%s\" has been successfully added\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
if(load_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
TOTP_CLI_PRINTF("Token \"%s\" has been successfully added\r\n", token_info->name);
}
@@ -93,14 +93,17 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args,
plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node);
plugin_state->tokens_count--;
totp_full_save_config_file(plugin_state);
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF("Token \"%s\" has been successfully deleted\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
token_info_free(token_info);
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
TOTP_CLI_PRINTF("Token \"%s\" has been successfully deleted\r\n", token_info->name);
token_info_free(token_info);
} else {
TOTP_CLI_PRINTF("User not confirmed\r\n");
}
@@ -147,18 +147,18 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C
}
if(token_updated) {
totp_full_save_config_file(plugin_state);
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
}
if(activate_generate_token_scene) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
if(token_updated) {
TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name);
} else {
TOTP_CLI_PRINT_INVALID_ARGUMENTS();
}
furi_string_free(temp_str);
}
@@ -86,15 +86,18 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString*
}
plugin_state->notification_method = new_method;
totp_config_file_update_notification_method(new_method);
if(totp_config_file_update_notification_method(new_method) ==
TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF("Notification method is set to ");
totp_cli_command_notification_print_method(new_method);
cli_nl();
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
if(previous_scene != TotpSceneNone) {
totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
}
TOTP_CLI_PRINTF("Notification method is set to ");
totp_cli_command_notification_print_method(new_method);
cli_nl();
} else {
TOTP_CLI_PRINTF("Current notification method is ");
totp_cli_command_notification_print_method(plugin_state->notification_method);
@@ -134,8 +134,14 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
plugin_state->crypto_verify_data = NULL;
}
totp_crypto_seed_iv(
plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length);
if(!totp_crypto_seed_iv(
plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length)) {
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
break;
}
memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE);
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
TokenInfo* token_info = node->data;
@@ -152,15 +158,18 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl
free(plain_token);
});
totp_full_save_config_file(plugin_state);
TOTP_CLI_DELETE_LAST_LINE();
if(do_change) {
TOTP_CLI_PRINTF("PIN has been successfully changed\r\n");
} else if(do_remove) {
TOTP_CLI_PRINTF("PIN has been successfully removed\r\n");
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
if(do_change) {
TOTP_CLI_PRINTF("PIN has been successfully changed\r\n");
} else if(do_remove) {
TOTP_CLI_PRINTF("PIN has been successfully removed\r\n");
}
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
} while(false);
if(load_generate_token_scene) {
@@ -33,8 +33,11 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
float tz = strtof(furi_string_get_cstr(temp_str), NULL);
if(tz >= -12.75f && tz <= 12.75f) {
plugin_state->timezone_offset = tz;
totp_config_file_update_timezone_offset(tz);
TOTP_CLI_PRINTF("Timezone is set to %f\r\n", tz);
if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF("Timezone is set to %f\r\n", tz);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
if(plugin_state->current_scene == TotpSceneGenerateToken) {
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
+431 -164
View File
@@ -6,9 +6,12 @@
#include "../../types/token_info.h"
#include "migrations/config_migration_v1_to_v2.h"
#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps/Misc"
#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator")
#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf"
#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup"
#define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp"
#define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig"
#define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf"
static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
switch(token_info->algo) {
@@ -35,15 +38,38 @@ static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString
}
}
Storage* totp_open_storage() {
/**
* @brief Opens storage record
* @return Storage record
*/
static Storage* totp_open_storage() {
return furi_record_open(RECORD_STORAGE);
}
void totp_close_storage() {
/**
* @brief Closes storage record
*/
static void totp_close_storage() {
furi_record_close(RECORD_STORAGE);
}
FlipperFormat* totp_open_config_file(Storage* storage) {
/**
* @brief Closes config file
* @param file config file reference
*/
static void totp_close_config_file(FlipperFormat* file) {
if(file == NULL) return;
flipper_format_file_close(file);
flipper_format_free(file);
}
/**
* @brief Opens or creates TOTP application standard config file
* @param storage storage record to use
* @param[out] file opened config file
* @return Config file open result
*/
static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) {
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) {
@@ -51,8 +77,28 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) {
FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH);
totp_close_config_file(fff_data_file);
return NULL;
return TotpConfigFileOpenError;
}
} else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) {
FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS);
if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
FURI_LOG_D(
LOGGING_TAG,
"Directory %s doesn't exist. Will create new.",
CONFIG_FILE_DIRECTORY_PATH);
if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
totp_close_config_file(fff_data_file);
return TotpConfigFileOpenError;
}
}
if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) {
FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH);
totp_close_config_file(fff_data_file);
return TotpConfigFileOpenError;
}
FURI_LOG_I(LOGGING_TAG, "Applied config file path migration");
return totp_open_config_file(storage, file);
} else {
FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH);
if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
@@ -62,14 +108,14 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
CONFIG_FILE_DIRECTORY_PATH);
if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
return NULL;
return TotpConfigFileOpenError;
}
}
if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) {
totp_close_config_file(fff_data_file);
FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH);
return NULL;
return TotpConfigFileOpenError;
}
flipper_format_write_header_cstr(
@@ -134,228 +180,415 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
if(!flipper_format_rewind(fff_data_file)) {
totp_close_config_file(fff_data_file);
FURI_LOG_E(LOGGING_TAG, "Rewind error");
return NULL;
return TotpConfigFileOpenError;
}
}
return fff_data_file;
*file = fff_data_file;
return TotpConfigFileOpenSuccess;
}
void totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) {
flipper_format_seek_to_end(file);
flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name);
bool token_is_valid = token_info->token != NULL && token_info->token_length > 0;
if(!token_is_valid) {
flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!");
TotpConfigFileUpdateResult
totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) {
TotpConfigFileUpdateResult update_result;
do {
if(!flipper_format_seek_to_end(file)) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) {
update_result = TotpConfigFileUpdateError;
break;
}
bool token_is_valid = token_info->token != NULL && token_info->token_length > 0;
if(!token_is_valid &&
!flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_hex(
file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_string_cstr(
file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) {
update_result = TotpConfigFileUpdateError;
break;
}
uint32_t tmp_uint32 = token_info->digits;
if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
return update_result;
}
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file;
TotpConfigFileUpdateResult update_result;
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
do {
if(totp_config_file_save_new_token_i(file, token_info) !=
TotpConfigFileUpdateSuccess) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
totp_close_config_file(file);
} else {
update_result = TotpConfigFileUpdateError;
}
flipper_format_write_hex(
file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length);
if(!token_is_valid) {
flipper_format_write_comment_cstr(file, "!!! WARNING END !!!");
totp_close_storage();
return update_result;
}
TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file;
TotpConfigFileUpdateResult update_result;
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
do {
if(!flipper_format_insert_or_update_float(
file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
totp_close_config_file(file);
} else {
update_result = TotpConfigFileUpdateError;
}
flipper_format_write_string_cstr(
file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info));
uint32_t tmp_uint32 = token_info->digits;
flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1);
}
void totp_config_file_save_new_token(const TokenInfo* token_info) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file = totp_open_config_file(cfg_storage);
totp_config_file_save_new_token_i(file, token_info);
totp_close_config_file(file);
totp_close_storage();
return update_result;
}
void totp_config_file_update_timezone_offset(float new_timezone_offset) {
TotpConfigFileUpdateResult
totp_config_file_update_notification_method(NotificationMethod new_notification_method) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file = totp_open_config_file(cfg_storage);
FlipperFormat* file;
TotpConfigFileUpdateResult update_result;
flipper_format_insert_or_update_float(file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1);
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
do {
uint32_t tmp_uint32 = new_notification_method;
if(!flipper_format_insert_or_update_uint32(
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
totp_close_config_file(file);
} else {
update_result = TotpConfigFileUpdateError;
}
totp_close_config_file(file);
totp_close_storage();
return update_result;
}
void totp_config_file_update_notification_method(NotificationMethod new_notification_method) {
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file = totp_open_config_file(cfg_storage);
FlipperFormat* file;
TotpConfigFileUpdateResult update_result;
if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
do {
if(!flipper_format_insert_or_update_float(
file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
uint32_t tmp_uint32 = plugin_state->notification_method;
if(!flipper_format_insert_or_update_uint32(
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
uint32_t tmp_uint32 = new_notification_method;
flipper_format_insert_or_update_uint32(
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
update_result = TotpConfigFileUpdateSuccess;
} while(false);
totp_close_config_file(file);
} else {
update_result = TotpConfigFileUpdateError;
}
totp_close_config_file(file);
totp_close_storage();
return update_result;
}
void totp_config_file_update_user_settings(const PluginState* plugin_state) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file = totp_open_config_file(cfg_storage);
flipper_format_insert_or_update_float(
file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1);
uint32_t tmp_uint32 = plugin_state->notification_method;
flipper_format_insert_or_update_uint32(
file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
totp_close_config_file(file);
totp_close_storage();
}
void totp_full_save_config_file(const PluginState* const plugin_state) {
TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) {
Storage* storage = totp_open_storage();
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess;
flipper_format_file_open_always(fff_data_file, CONFIG_FILE_PATH);
flipper_format_write_header_cstr(
fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION);
flipper_format_write_hex(
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE);
flipper_format_write_hex(
fff_data_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
plugin_state->crypto_verify_data_length);
flipper_format_write_float(
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1);
flipper_format_write_bool(fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1);
uint32_t tmp_uint32 = plugin_state->notification_method;
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
do {
if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) {
result = TotpConfigFileUpdateError;
break;
}
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
const TokenInfo* token_info = node->data;
totp_config_file_save_new_token_i(fff_data_file, token_info);
});
if(!flipper_format_write_header_cstr(
fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) {
result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_hex(
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_hex(
fff_data_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
plugin_state->crypto_verify_data_length)) {
result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_float(
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_write_bool(
fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
result = TotpConfigFileUpdateError;
break;
}
uint32_t tmp_uint32 = plugin_state->notification_method;
if(!flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
result = TotpConfigFileUpdateError;
break;
}
bool tokens_written = true;
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
const TokenInfo* token_info = node->data;
tokens_written = tokens_written &&
totp_config_file_save_new_token_i(fff_data_file, token_info) ==
TotpConfigFileUpdateSuccess;
});
if(!tokens_written) {
result = TotpConfigFileUpdateError;
break;
}
} while(false);
totp_close_config_file(fff_data_file);
if(result == TotpConfigFileUpdateSuccess) {
if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) {
storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH);
}
if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) {
result = TotpConfigFileUpdateError;
} else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) {
result = TotpConfigFileUpdateError;
} else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) {
result = TotpConfigFileUpdateError;
}
}
totp_close_storage();
return result;
}
void totp_config_file_load_base(PluginState* const plugin_state) {
TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) {
Storage* storage = totp_open_storage();
FlipperFormat* fff_data_file = totp_open_config_file(storage);
FlipperFormat* fff_data_file;
TotpConfigFileOpenResult result;
if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) {
totp_close_storage();
return result;
}
plugin_state->timezone_offset = 0;
FuriString* temp_str = furi_string_alloc();
uint32_t file_version;
if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) {
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
furi_string_free(temp_str);
return;
}
if(file_version < CONFIG_FILE_ACTUAL_VERSION) {
FURI_LOG_I(
LOGGING_TAG,
"Obsolete config file version detected. Current version: %" PRIu32
"; Actual version: %" PRId16,
file_version,
CONFIG_FILE_ACTUAL_VERSION);
totp_close_config_file(fff_data_file);
if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) {
storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH);
do {
uint32_t file_version;
if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) {
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
result = TotpConfigFileOpenError;
break;
}
if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) {
FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH);
fff_data_file = totp_open_config_file(storage);
FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
flipper_format_file_open_existing(fff_backup_data_file, CONFIG_FILE_BACKUP_PATH);
if(file_version < CONFIG_FILE_ACTUAL_VERSION) {
FURI_LOG_I(
LOGGING_TAG,
"Obsolete config file version detected. Current version: %" PRIu32
"; Actual version: %" PRId16,
file_version,
CONFIG_FILE_ACTUAL_VERSION);
totp_close_config_file(fff_data_file);
if(file_version == 1) {
if(totp_config_migrate_v1_to_v2(fff_data_file, fff_backup_data_file)) {
FURI_LOG_I(LOGGING_TAG, "Applied migration from v1 to v2");
} else {
FURI_LOG_W(LOGGING_TAG, "An error occurred during migration from v1 to v2");
}
if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) {
storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH);
}
flipper_format_file_close(fff_backup_data_file);
flipper_format_free(fff_backup_data_file);
flipper_format_rewind(fff_data_file);
} else {
FURI_LOG_E(
LOGGING_TAG,
"An error occurred during taking backup of %s into %s before migration",
CONFIG_FILE_PATH,
CONFIG_FILE_BACKUP_PATH);
if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) {
FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH);
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
result = TotpConfigFileOpenError;
break;
}
FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage);
if(!flipper_format_file_open_existing(
fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) {
flipper_format_file_close(fff_backup_data_file);
flipper_format_free(fff_backup_data_file);
result = TotpConfigFileOpenError;
break;
}
if(file_version == 1) {
if(totp_config_migrate_v1_to_v2(fff_data_file, fff_backup_data_file)) {
FURI_LOG_I(LOGGING_TAG, "Applied migration from v1 to v2");
} else {
FURI_LOG_W(
LOGGING_TAG, "An error occurred during migration from v1 to v2");
result = TotpConfigFileOpenError;
break;
}
}
flipper_format_file_close(fff_backup_data_file);
flipper_format_free(fff_backup_data_file);
flipper_format_rewind(fff_data_file);
} else {
FURI_LOG_E(
LOGGING_TAG,
"An error occurred during taking backup of %s into %s before migration",
CONFIG_FILE_PATH,
CONFIG_FILE_BACKUP_PATH);
result = TotpConfigFileOpenError;
break;
}
}
}
if(!flipper_format_read_hex(
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
FURI_LOG_D(LOGGING_TAG, "Missing base IV");
}
flipper_format_rewind(fff_data_file);
uint32_t crypto_size;
if(flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) &&
crypto_size > 0) {
plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = crypto_size;
if(!flipper_format_read_hex(
fff_data_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
crypto_size)) {
FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
free(plugin_state->crypto_verify_data);
fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) {
FURI_LOG_D(LOGGING_TAG, "Missing base IV");
}
if(!flipper_format_rewind(fff_data_file)) {
result = TotpConfigFileOpenError;
break;
}
uint32_t crypto_size;
if(flipper_format_get_value_count(
fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) &&
crypto_size > 0) {
plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size);
furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = crypto_size;
if(!flipper_format_read_hex(
fff_data_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
crypto_size)) {
FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token");
free(plugin_state->crypto_verify_data);
plugin_state->crypto_verify_data = NULL;
plugin_state->crypto_verify_data_length = 0;
}
} else {
plugin_state->crypto_verify_data = NULL;
plugin_state->crypto_verify_data_length = 0;
}
} else {
plugin_state->crypto_verify_data = NULL;
plugin_state->crypto_verify_data_length = 0;
}
flipper_format_rewind(fff_data_file);
if(!flipper_format_rewind(fff_data_file)) {
result = TotpConfigFileOpenError;
break;
}
if(!flipper_format_read_float(
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
plugin_state->timezone_offset = 0;
FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0");
}
if(!flipper_format_read_float(
fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) {
plugin_state->timezone_offset = 0;
FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0");
}
flipper_format_rewind(fff_data_file);
if(!flipper_format_rewind(fff_data_file)) {
result = TotpConfigFileOpenError;
break;
}
if(!flipper_format_read_bool(
fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
plugin_state->pin_set = true;
}
if(!flipper_format_read_bool(
fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
plugin_state->pin_set = true;
}
flipper_format_rewind(fff_data_file);
flipper_format_rewind(fff_data_file);
uint32_t tmp_uint32;
if(!flipper_format_read_uint32(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
}
uint32_t tmp_uint32;
if(!flipper_format_read_uint32(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) {
tmp_uint32 = NotificationMethodSound | NotificationMethodVibro;
}
plugin_state->notification_method = tmp_uint32;
plugin_state->notification_method = tmp_uint32;
} while(false);
furi_string_free(temp_str);
totp_close_config_file(fff_data_file);
totp_close_storage();
return result;
}
TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) {
Storage* storage = totp_open_storage();
FlipperFormat* fff_data_file = totp_open_config_file(storage);
FlipperFormat* fff_data_file;
if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) {
totp_close_storage();
return TokenLoadingResultError;
}
FuriString* temp_str = furi_string_alloc();
uint32_t temp_data32;
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header");
totp_close_storage();
furi_string_free(temp_str);
return TokenLoadingResultError;
}
@@ -459,8 +692,42 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
return result;
}
void totp_close_config_file(FlipperFormat* file) {
if(file == NULL) return;
flipper_format_file_close(file);
flipper_format_free(file);
}
TotpConfigFileUpdateResult
totp_config_file_update_crypto_signatures(const PluginState* plugin_state) {
Storage* storage = totp_open_storage();
FlipperFormat* config_file;
TotpConfigFileUpdateResult update_result;
if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) {
do {
if(!flipper_format_insert_or_update_hex(
config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_insert_or_update_hex(
config_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
plugin_state->crypto_verify_data_length)) {
update_result = TotpConfigFileUpdateError;
break;
}
if(!flipper_format_insert_or_update_bool(
config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) {
update_result = TotpConfigFileUpdateError;
break;
}
update_result = TotpConfigFileUpdateSuccess;
} while(false);
totp_close_config_file(config_file);
} else {
update_result = TotpConfigFileUpdateError;
}
totp_close_storage();
return update_result;
}
@@ -7,6 +7,8 @@
#include "constants.h"
typedef uint8_t TokenLoadingResult;
typedef uint8_t TotpConfigFileOpenResult;
typedef uint8_t TotpConfigFileUpdateResult;
/**
* @brief Token loading results
@@ -29,40 +31,48 @@ enum TokenLoadingResults {
};
/**
* @brief Opens storage record
* @return Storage record
* @brief Config file opening result
*/
Storage* totp_open_storage();
enum TotpConfigFileOpenResults {
/**
* @brief Config file opened successfully
*/
TotpConfigFileOpenSuccess = 0,
/**
* @brief An error has occurred during opening config file
*/
TotpConfigFileOpenError = 1
};
/**
* @brief Closes storage record
* @brief Config file updating result
*/
void totp_close_storage();
enum TotpConfigFileUpdateResults {
/**
* @brief Config file updated successfully
*/
TotpConfigFileUpdateSuccess,
/**
* @brief Opens or creates TOTP application standard config file
* @param storage storage record to use
* @return Config file reference
*/
FlipperFormat* totp_open_config_file(Storage* storage);
/**
* @brief Closes config file
* @param file config file reference
*/
void totp_close_config_file(FlipperFormat* file);
/**
* @brief An error has occurred during updating config file
*/
TotpConfigFileUpdateError
};
/**
* @brief Saves all the settings and tokens to an application config file
* @param plugin_state application state
* @return Config file update result
*/
void totp_full_save_config_file(const PluginState* const plugin_state);
TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state);
/**
* @brief Loads basic information from an application config file into application state without loading all the tokens
* @param plugin_state application state
* @return Config file open result
*/
void totp_config_file_load_base(PluginState* const plugin_state);
TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state);
/**
* @brief Loads tokens from an application config file into application state
@@ -74,23 +84,36 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
/**
* @brief Add new token to the end of the application config file
* @param token_info token information to be saved
* @return Config file update result
*/
void totp_config_file_save_new_token(const TokenInfo* token_info);
TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info);
/**
* @brief Updates timezone offset in an application config file
* @param new_timezone_offset new timezone offset to be set
* @return Config file update result
*/
void totp_config_file_update_timezone_offset(float new_timezone_offset);
TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset);
/**
* @brief Updates notification method in an application config file
* @param new_notification_method new notification method to be set
* @return Config file update result
*/
void totp_config_file_update_notification_method(NotificationMethod new_notification_method);
TotpConfigFileUpdateResult
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
/**
* @brief Updates application user settings
* @param plugin_state application state
* @return Config file update result
*/
void totp_config_file_update_user_settings(const PluginState* plugin_state);
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state);
/**
* @brief Updates crypto signatures information
* @param plugin_state application state
* @return Config file update result
*/
TotpConfigFileUpdateResult
totp_config_file_update_crypto_signatures(const PluginState* plugin_state);
@@ -61,7 +61,7 @@ uint8_t* totp_crypto_decrypt(
return decrypted_data;
}
void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) {
if(plugin_state->crypto_verify_data == NULL) {
FURI_LOG_D(LOGGING_TAG, "Generating new IV");
furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE);
@@ -94,13 +94,12 @@ void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t
}
}
bool result = true;
if(plugin_state->crypto_verify_data == NULL) {
FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data");
plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH);
furi_check(plugin_state->crypto_verify_data != NULL);
plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH;
Storage* storage = totp_open_storage();
FlipperFormat* config_file = totp_open_config_file(storage);
plugin_state->crypto_verify_data = totp_crypto_encrypt(
(uint8_t*)CRYPTO_VERIFY_KEY,
@@ -108,19 +107,13 @@ void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t
&plugin_state->iv[0],
&plugin_state->crypto_verify_data_length);
flipper_format_insert_or_update_hex(
config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE);
flipper_format_insert_or_update_hex(
config_file,
TOTP_CONFIG_KEY_CRYPTO_VERIFY,
plugin_state->crypto_verify_data,
CRYPTO_VERIFY_KEY_LENGTH);
plugin_state->pin_set = pin != NULL && pin_length > 0;
flipper_format_insert_or_update_bool(
config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1);
totp_close_config_file(config_file);
totp_close_storage();
result = totp_config_file_update_crypto_signatures(plugin_state) ==
TotpConfigFileUpdateSuccess;
}
return result;
}
bool totp_crypto_verify_key(const PluginState* plugin_state) {
@@ -35,8 +35,9 @@ uint8_t* totp_crypto_decrypt(
* @param plugin_state application state
* @param pin user's PIN
* @param pin_length user's PIN length
* @return \c true on success; \c false otherwise
*/
void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length);
/**
* @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption
@@ -18,8 +18,6 @@
/* Written by Simon Josefsson. The interface was inspired by memxor
in Niels Möller's Nettle. */
/* #include <config.h> */
#include "memxor.h"
void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n) {
+35 -19
View File
@@ -15,6 +15,7 @@
#include "types/common.h"
#include "ui/scene_director.h"
#include "ui/constants.h"
#include "ui/common_dialogs.h"
#include "services/crypto/crypto.h"
#include "cli/cli.h"
@@ -36,17 +37,7 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static bool totp_plugin_state_init(PluginState* const plugin_state) {
plugin_state->gui = furi_record_open(RECORD_GUI);
plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
totp_config_file_load_base(plugin_state);
totp_cli_register_command_handler(plugin_state);
totp_scene_director_init_scenes(plugin_state);
static bool totp_activate_initial_scene(PluginState* const plugin_state) {
if(plugin_state->crypto_verify_data == NULL) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "No", NULL, "Yes");
@@ -63,13 +54,19 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
if(dialog_result == DialogMessageButtonRight) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else {
totp_crypto_seed_iv(plugin_state, NULL, 0);
if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
} else if(plugin_state->pin_set) {
totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL);
} else {
totp_crypto_seed_iv(plugin_state, NULL, 0);
if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
if(totp_crypto_verify_key(plugin_state)) {
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
} else {
@@ -94,13 +91,20 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
return true;
}
static bool totp_plugin_state_init(PluginState* const plugin_state) {
plugin_state->gui = furi_record_open(RECORD_GUI);
plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION);
plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS);
if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) {
totp_dialogs_config_loading_error(plugin_state);
return false;
}
return true;
}
static void totp_plugin_state_free(PluginState* plugin_state) {
totp_cli_unregister_command_handler();
totp_scene_director_deactivate_active_scene(plugin_state);
totp_scene_director_dispose(plugin_state);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
@@ -139,6 +143,14 @@ int32_t totp_app() {
return 255;
}
totp_cli_register_command_handler(plugin_state);
totp_scene_director_init_scenes(plugin_state);
if(!totp_activate_initial_scene(plugin_state)) {
FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n");
totp_plugin_state_free(plugin_state);
return 253;
}
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
@@ -171,6 +183,10 @@ int32_t totp_app() {
release_mutex(&state_mutex, plugin_state_m);
}
totp_cli_unregister_command_handler();
totp_scene_director_deactivate_active_scene(plugin_state);
totp_scene_director_dispose(plugin_state);
view_port_enabled_set(view_port, false);
gui_remove_view_port(plugin_state->gui, view_port);
view_port_free(view_port);
@@ -0,0 +1,20 @@
#include "common_dialogs.h"
#include "constants.h"
static DialogMessageButton totp_dialogs_common(PluginState* plugin_state, const char* text) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_buttons(message, "Exit", NULL, NULL);
dialog_message_set_text(
message, text, SCREEN_WIDTH_CENTER, SCREEN_HEIGHT_CENTER, AlignCenter, AlignCenter);
DialogMessageButton result = dialog_message_show(plugin_state->dialogs_app, message);
dialog_message_free(message);
return result;
}
DialogMessageButton totp_dialogs_config_loading_error(PluginState* plugin_state) {
return totp_dialogs_common(plugin_state, "An error has occurred\nduring loading config file");
}
DialogMessageButton totp_dialogs_config_updating_error(PluginState* plugin_state) {
return totp_dialogs_common(plugin_state, "An error has occurred\nduring updating config file");
}
@@ -0,0 +1,7 @@
#pragma once
#include <dialogs/dialogs.h>
#include "../types/plugin_state.h"
DialogMessageButton totp_dialogs_config_loading_error(PluginState* plugin_state);
DialogMessageButton totp_dialogs_config_updating_error(PluginState* plugin_state);
@@ -7,6 +7,7 @@
#include "../../../lib/list/list.h"
#include "../../../services/config/config.h"
#include "../../ui_controls.h"
#include "../../common_dialogs.h"
#include "../../../lib/roll_value/roll_value.h"
#include "../../../types/nullable.h"
#include "../generate_token/totp_scene_generate_token.h"
@@ -248,7 +249,11 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check);
plugin_state->tokens_count++;
totp_config_file_save_new_token(tokenInfo);
if(totp_config_file_save_new_token(tokenInfo) != TotpConfigFileUpdateSuccess) {
token_info_free(tokenInfo);
totp_dialogs_config_updating_error(plugin_state);
return false;
}
GenerateTokenSceneContext generate_scene_context = {
.current_token_index = plugin_state->tokens_count - 1};
@@ -2,6 +2,7 @@
#include <math.h>
#include <totp_icons.h>
#include "../../ui_controls.h"
#include "../../common_dialogs.h"
#include "../../scene_director.h"
#include "../token_menu/totp_scene_token_menu.h"
#include "../../constants.h"
@@ -202,7 +203,12 @@ bool totp_scene_app_settings_handle_event(
NotificationMethodNone) |
(scene_state->notification_vibro ? NotificationMethodVibro :
NotificationMethodNone);
totp_config_file_update_user_settings(plugin_state);
if(totp_config_file_update_user_settings(plugin_state) !=
TotpConfigFileUpdateSuccess) {
totp_dialogs_config_updating_error(plugin_state);
return false;
}
if(!scene_state->current_token_index.is_null) {
TokenMenuSceneContext generate_scene_context = {
@@ -128,7 +128,7 @@ static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount
str[len] = '\0';
}
TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
switch(algo) {
case SHA1:
return TOTP_ALGO_SHA1;
@@ -143,7 +143,7 @@ TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
return NULL;
}
void update_totp_params(PluginState* const plugin_state) {
static void update_totp_params(PluginState* const plugin_state) {
SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
if(scene_state->current_token_index < plugin_state->tokens_count) {
@@ -2,6 +2,7 @@
#include <gui/gui.h>
#include <dialogs/dialogs.h>
#include "../../ui_controls.h"
#include "../../common_dialogs.h"
#include "../../constants.h"
#include "../../scene_director.h"
#include "../../../services/config/config.h"
@@ -156,7 +157,10 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt
furi_check(tokenInfo != NULL);
token_info_free(tokenInfo);
totp_full_save_config_file(plugin_state);
if(totp_full_save_config_file(plugin_state) != TotpConfigFileUpdateSuccess) {
totp_dialogs_config_updating_error(plugin_state);
return false;
}
totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
}
break;
+1 -1
View File
@@ -4,6 +4,6 @@
[![GitHub](https://img.shields.io/github/license/quen0n/unitemp-flipperzero)](https://github.com/quen0n/unitemp-flipperzero/blob/dev/LICENSE.md)
[Flipper Zero](https://flipperzero.one/) application for reading temperature, humidity and pressure sensors using Onewire, Singlewire, I2C protocols.
## List of supported sensors (supplemented)
![image](https://user-images.githubusercontent.com/10090793/208763931-d15e9883-1016-4add-bd00-14d7842fd82d.png)
![image](https://user-images.githubusercontent.com/10090793/209491886-f4c5ef6e-38b2-45b8-a8e7-4aeca9e155f2.png)
## Installation
Copy the contents of the repository to the `applications/plugins/unitemp` folder and build the project. Flash FZ along with resources. [More...](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md)
+2
View File
@@ -81,6 +81,8 @@ static const SensorType* sensorTypes[] = {
&AM2320_SW,
&AM2320_I2C,
&AHT10,
&SHT30,
&GXHT30,
&LM75,
&BMP280,
&BME280};
+1
View File
@@ -322,4 +322,5 @@ const GPIO*
#include "./sensors/BMx280.h"
#include "./sensors/AM2320.h"
#include "./sensors/DHT20.h"
#include "./sensors/SHT30.h"
#endif
@@ -0,0 +1,90 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "SHT30.h"
#include "../interfaces/I2CSensor.h"
const SensorType SHT30 = {
.typename = "SHT30",
.altname = "SHT30/31/35",
.interface = &I2C,
.datatype = UT_TEMPERATURE | UT_HUMIDITY,
.pollingInterval = 1000,
.allocator = unitemp_SHT30_I2C_alloc,
.mem_releaser = unitemp_SHT30_I2C_free,
.initializer = unitemp_SHT30_init,
.deinitializer = unitemp_SHT30_I2C_deinit,
.updater = unitemp_SHT30_I2C_update};
const SensorType GXHT30 = {
.typename = "GXHT30",
.altname = "GXHT30/31/35",
.interface = &I2C,
.datatype = UT_TEMPERATURE | UT_HUMIDITY,
.pollingInterval = 1000,
.allocator = unitemp_SHT30_I2C_alloc,
.mem_releaser = unitemp_SHT30_I2C_free,
.initializer = unitemp_GXHT30_init,
.deinitializer = unitemp_SHT30_I2C_deinit,
.updater = unitemp_SHT30_I2C_update};
bool unitemp_SHT30_I2C_alloc(Sensor* sensor, char* args) {
UNUSED(args);
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
//Адреса на шине I2C (7 бит)
i2c_sensor->minI2CAdr = 0x44 << 1;
i2c_sensor->maxI2CAdr = 0x45 << 1;
return true;
}
bool unitemp_SHT30_I2C_free(Sensor* sensor) {
//Нечего высвобождать, так как ничего не было выделено
UNUSED(sensor);
return true;
}
bool unitemp_SHT30_init(Sensor* sensor) {
UNUSED(sensor);
return true;
}
bool unitemp_GXHT30_init(Sensor* sensor) {
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
//Включение режима автоматического преобразования 2 раза в сек
uint8_t data[2] = {0x22, 0x36};
if(!unitemp_i2c_writeArray(i2c_sensor, 2, data)) return false;
return true;
}
bool unitemp_SHT30_I2C_deinit(Sensor* sensor) {
//Нечего деинициализировать
UNUSED(sensor);
return true;
}
UnitempStatus unitemp_SHT30_I2C_update(Sensor* sensor) {
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
//Получение данных
uint8_t data[6] = {0xE0, 0x00};
if(!unitemp_i2c_writeArray(i2c_sensor, 2, data)) return UT_SENSORSTATUS_TIMEOUT;
if(!unitemp_i2c_readArray(i2c_sensor, 6, data)) return UT_SENSORSTATUS_TIMEOUT;
sensor->temp = -45 + 175 * (((uint16_t)(data[0] << 8) | data[1]) / 65535.0f);
sensor->hum = 100 * (((uint16_t)(data[3] << 8) | data[4]) / 65535.0f);
return UT_SENSORSTATUS_OK;
}
@@ -0,0 +1,70 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UNITEMP_SHT30
#define UNITEMP_SHT30
#include "../unitemp.h"
#include "../Sensors.h"
extern const SensorType SHT30;
extern const SensorType GXHT30;
/**
* @brief Выделение памяти и установка начальных значений датчика SHT30
*
* @param sensor Указатель на создаваемый датчик
* @return Истина при успехе
*/
bool unitemp_SHT30_I2C_alloc(Sensor* sensor, char* args);
/**
* @brief Инициализации датчика SHT30
*
* @param sensor Указатель на датчик
* @return Истина если инициализация упспешная
*/
bool unitemp_SHT30_init(Sensor* sensor);
/**
* @brief Инициализации датчика GXHT30
*
* @param sensor Указатель на датчик
* @return Истина если инициализация упспешная
*/
bool unitemp_GXHT30_init(Sensor* sensor);
/**
* @brief Деинициализация датчика
*
* @param sensor Указатель на датчик
*/
bool unitemp_SHT30_I2C_deinit(Sensor* sensor);
/**
* @brief Обновление значений из датчика
*
* @param sensor Указатель на датчик
* @return Статус обновления
*/
UnitempStatus unitemp_SHT30_I2C_update(Sensor* sensor);
/**
* @brief Высвободить память датчика
*
* @param sensor Указатель на датчик
*/
bool unitemp_SHT30_I2C_free(Sensor* sensor);
#endif
Binary file not shown.
@@ -132,7 +132,7 @@ void unitemp_widget_delete_switch(Sensor* sensor) {
app->buff,
BUFF_SIZE,
"\e#I2C addr:\e# 0x%02X",
((I2CSensor*)current_sensor->instance)->currentI2CAdr);
((I2CSensor*)current_sensor->instance)->currentI2CAdr >> 1);
widget_add_text_box_element(
app->widget, 0, 28, 128, 23, AlignLeft, AlignTop, app->buff, false);
}
@@ -343,7 +343,7 @@ bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipp
flipper_format,
"VarData",
(uint8_t*)&instance->var_data,
sizeof(instance->var_data))) {
sizeof(instance->var_data))) { //-V1051
FURI_LOG_E(TAG, "Missing VarData");
break;
}
@@ -147,7 +147,7 @@ static void ws_protocol_oregon_v1_remote_controller(WSBlockGeneric* instance) {
instance->temp = -temp_raw;
}
instance->battery_low = !(instance->data >> 23) & 1;
instance->battery_low = !((instance->data >> 23) & 1ULL);
instance->btn = WS_NO_BTN;
instance->humidity = WS_NO_HUMIDITY;
@@ -79,7 +79,7 @@ bool ws_block_generic_serialize(
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF;
key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF;
}
if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
@@ -208,4 +208,4 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp
} while(0);
return res;
}
}
@@ -303,7 +303,7 @@ bool ws_view_receiver_input(InputEvent* event, void* context) {
ws_receiver->view,
WSReceiverModel * model,
{
if(model->idx != model->history_item - 1) model->idx++;
if(model->history_item && model->idx != model->history_item - 1) model->idx++;
},
true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
@@ -4,8 +4,7 @@
#include "../protocols/ws_generic.h"
#include <input/input.h>
#include <gui/elements.h>
#define abs(x) ((x) > 0 ? (x) : -(x))
#include <float_tools.h>
struct WSReceiverInfo {
View* view;
@@ -79,7 +78,7 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
elements_bold_rounded_frame(canvas, 0, 38, 127, 25);
canvas_set_font(canvas, FontPrimary);
if(model->generic->temp != WS_NO_TEMPERATURE) {
if(!float_is_equal(model->generic->temp, WS_NO_TEMPERATURE)) {
canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16);
uint8_t temp_x1 = 0;
@@ -111,11 +111,8 @@ void ws_hopper_update(WeatherStationApp* app) {
switch(app->txrx->hopper_state) {
case WSHopperStateOFF:
return;
break;
case WSHopperStatePause:
return;
break;
case WSHopperStateRSSITimeOut:
if(app->txrx->hopper_timeout != 0) {
app->txrx->hopper_timeout--;
@@ -186,7 +186,7 @@ WSHistoryStateAddKey
}
// or add new record
if(!sensor_found) {
if(!sensor_found) { //-V547
WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data);
item->preset = malloc(sizeof(SubGhzRadioPreset));
item->type = decoder_base->protocol->type;
@@ -34,7 +34,7 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
app->text_box_store_strlen = 0;
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
const char* help_msg =
"Marauder companion v0.2.2\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n";
"Marauder companion v0.3.0\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n";
furi_string_cat_str(app->text_box_store, help_msg);
app->text_box_store_strlen += strlen(help_msg);
}
@@ -26,7 +26,13 @@ typedef struct {
// NUM_MENU_ITEMS defined in wifi_marauder_app_i.h - if you add an entry here, increment it!
const WifiMarauderItem items[NUM_MENU_ITEMS] = {
{"View Log from", {"start", "end"}, 2, {"", ""}, NO_ARGS, FOCUS_CONSOLE_TOGGLE, NO_TIP},
{"Scan AP", {""}, 1, {"scanap"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP},
{"Scan",
{"ap", "station"},
2,
{"scanap", "scansta"},
NO_ARGS,
FOCUS_CONSOLE_END,
SHOW_STOPSCAN_TIP},
{"SSID",
{"add rand", "add name", "remove"},
3,
@@ -34,12 +40,24 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
INPUT_ARGS,
FOCUS_CONSOLE_START,
NO_TIP},
{"List", {"ap", "ssid"}, 2, {"list -a", "list -s"}, NO_ARGS, FOCUS_CONSOLE_START, NO_TIP},
{"Select", {"ap", "ssid"}, 2, {"select -a", "select -s"}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"List",
{"ap", "ssid", "station"},
3,
{"list -a", "list -s", "list -c"},
NO_ARGS,
FOCUS_CONSOLE_START,
NO_TIP},
{"Select",
{"ap", "ssid", "station"},
3,
{"select -a", "select -s", "select -c"},
INPUT_ARGS,
FOCUS_CONSOLE_END,
NO_TIP},
{"Clear List",
{"ap", "ssid"},
2,
{"clearlist -a", "clearlist -s"},
{"ap", "ssid", "station"},
3,
{"clearlist -a", "clearlist -s", "clearlist -c"},
NO_ARGS,
FOCUS_CONSOLE_END,
NO_TIP},
@@ -50,6 +68,13 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
NO_ARGS,
FOCUS_CONSOLE_END,
SHOW_STOPSCAN_TIP},
{"Targeted Deauth",
{"station", "manual"},
2,
{"attack -t deauth -c", "attack -t deauth -s"},
TOGGLE_ARGS,
FOCUS_CONSOLE_END,
SHOW_STOPSCAN_TIP},
{"Beacon Spam",
{"ap list", "ssid list", "random"},
3,
@@ -87,18 +112,19 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
FOCUS_CONSOLE_END,
NO_TIP},
{"Settings",
{"display", "restore", "ForcePMKID", "ForceProbe", "SavePCAP", "other"},
6,
{"display", "restore", "ForcePMKID", "ForceProbe", "SavePCAP", "EnableLED", "other"},
7,
{"settings",
"settings -r",
"settings -s ForcePMKID enable",
"settings -s ForceProbe enable",
"settings -s SavePCAP enable",
"settings -s EnableLED enable",
"settings -s"},
TOGGLE_ARGS,
FOCUS_CONSOLE_START,
NO_TIP},
{"Update", {""}, 1, {"update -w"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
};
@@ -3,13 +3,34 @@
void wifi_marauder_scene_text_input_callback(void* context) {
WifiMarauderApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartConsole);
switch(app->special_case_input_step) {
case 0: // most commands
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartConsole);
break;
case 1: // special case for deauth: save source MAC
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventSaveSourceMac);
break;
case 2: // special case for deauth: save destination MAC
view_dispatcher_send_custom_event(
app->view_dispatcher, WifiMarauderEventSaveDestinationMac);
break;
default:
break;
}
}
void wifi_marauder_scene_text_input_on_enter(void* context) {
WifiMarauderApp* app = context;
if(false == app->is_custom_tx_string) {
if(0 ==
strncmp("attack -t deauth -s", app->selected_tx_string, strlen("attack -t deauth -s"))) {
// Special case for manual deauth input
app->special_case_input_step = 1;
bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE);
} else if(false == app->is_custom_tx_string) {
// Most commands
app->special_case_input_step = 0;
// Fill text input with selected string so that user can add to it
size_t length = strlen(app->selected_tx_string);
furi_assert(length < WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE);
@@ -25,7 +46,9 @@ void wifi_marauder_scene_text_input_on_enter(void* context) {
// Setup view
TextInput* text_input = app->text_input;
// Add help message to header
if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) {
if(app->special_case_input_step == 1) {
text_input_set_header_text(text_input, "Enter source MAC");
} else if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) {
text_input_set_header_text(text_input, "Enter # SSIDs to generate");
} else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) {
text_input_set_header_text(text_input, "Enter SSID name to add");
@@ -59,6 +82,65 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev
app->selected_tx_string = app->text_input_store;
scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
consumed = true;
} else if(event.event == WifiMarauderEventSaveSourceMac) {
if(12 != strlen(app->text_input_store)) {
text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!");
} else {
snprintf(
app->special_case_input_src_addr,
sizeof(app->special_case_input_src_addr),
"%c%c:%c%c:%c%c:%c%c:%c%c:%c%c",
app->text_input_store[0],
app->text_input_store[1],
app->text_input_store[2],
app->text_input_store[3],
app->text_input_store[4],
app->text_input_store[5],
app->text_input_store[6],
app->text_input_store[7],
app->text_input_store[8],
app->text_input_store[9],
app->text_input_store[10],
app->text_input_store[11]);
// Advance scene to input destination MAC, clear text input
app->special_case_input_step = 2;
bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE);
text_input_set_header_text(app->text_input, "Enter destination MAC");
}
consumed = true;
} else if(event.event == WifiMarauderEventSaveDestinationMac) {
if(12 != strlen(app->text_input_store)) {
text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!");
} else {
snprintf(
app->special_case_input_dst_addr,
sizeof(app->special_case_input_dst_addr),
"%c%c:%c%c:%c%c:%c%c:%c%c:%c%c",
app->text_input_store[0],
app->text_input_store[1],
app->text_input_store[2],
app->text_input_store[3],
app->text_input_store[4],
app->text_input_store[5],
app->text_input_store[6],
app->text_input_store[7],
app->text_input_store[8],
app->text_input_store[9],
app->text_input_store[10],
app->text_input_store[11]);
// Construct command with source and destination MACs
snprintf(
app->text_input_store,
WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE,
"attack -t deauth -s %18s -d %18s",
app->special_case_input_src_addr,
app->special_case_input_dst_addr);
app->selected_tx_string = app->text_input_store;
scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
}
consumed = true;
}
}
@@ -50,6 +50,8 @@ WifiMarauderApp* wifi_marauder_app_alloc() {
app->selected_option_index[i] = 0;
}
app->special_case_input_step = 0;
app->text_box = text_box_alloc();
view_dispatcher_add_view(
app->view_dispatcher, WifiMarauderAppViewConsoleOutput, text_box_get_view(app->text_box));
@@ -14,7 +14,7 @@
#include <gui/modules/text_input.h>
#include <gui/modules/variable_item_list.h>
#define NUM_MENU_ITEMS (15)
#define NUM_MENU_ITEMS (16)
#define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096)
#define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512)
@@ -41,6 +41,11 @@ struct WifiMarauderApp {
bool is_custom_tx_string;
bool focus_console_start;
bool show_stopscan_tip;
// For input source and destination MAC in targeted deauth attack
int special_case_input_step;
char special_case_input_src_addr[20];
char special_case_input_dst_addr[20];
};
// Supported commands:
@@ -4,4 +4,6 @@ typedef enum {
WifiMarauderEventRefreshConsoleOutput = 0,
WifiMarauderEventStartConsole,
WifiMarauderEventStartKeyboard,
WifiMarauderEventSaveSourceMac,
WifiMarauderEventSaveDestinationMac
} WifiMarauderCustomEvent;