More apps moved to apps repo (#2978)
* Clock, music player, snake game removed * Music player, picopass assets removed Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									d808884b97
								
							
						
					
					
						commit
						b368660d48
					
				@ -33,9 +33,6 @@ Applications for main Flipper menu.
 | 
				
			|||||||
- `nfc`                 - NFC application, HF rfid, EMV and etc
 | 
					- `nfc`                 - NFC application, HF rfid, EMV and etc
 | 
				
			||||||
- `subghz`              - SubGhz application, 433 fobs and etc
 | 
					- `subghz`              - SubGhz application, 433 fobs and etc
 | 
				
			||||||
- `u2f`                 - U2F Application
 | 
					- `u2f`                 - U2F Application
 | 
				
			||||||
- `clock`               - Clock application
 | 
					 | 
				
			||||||
- `music_player`        - Music player app (demo)
 | 
					 | 
				
			||||||
- `snake_game`          - Snake game application
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## services
 | 
					## services
 | 
				
			||||||
 | 
				
			|||||||
@ -11,9 +11,6 @@ App(
 | 
				
			|||||||
        "subghz",
 | 
					        "subghz",
 | 
				
			||||||
        "bad_usb",
 | 
					        "bad_usb",
 | 
				
			||||||
        "u2f",
 | 
					        "u2f",
 | 
				
			||||||
        "clock",
 | 
					 | 
				
			||||||
        "music_player",
 | 
					 | 
				
			||||||
        "snake_game",
 | 
					 | 
				
			||||||
        "archive",
 | 
					        "archive",
 | 
				
			||||||
        "main_apps_on_start",
 | 
					        "main_apps_on_start",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
App(
 | 
					 | 
				
			||||||
    appid="clock",
 | 
					 | 
				
			||||||
    name="Clock",
 | 
					 | 
				
			||||||
    apptype=FlipperAppType.EXTERNAL,
 | 
					 | 
				
			||||||
    entry_point="clock_app",
 | 
					 | 
				
			||||||
    requires=["gui"],
 | 
					 | 
				
			||||||
    stack_size=2 * 1024,
 | 
					 | 
				
			||||||
    fap_icon="clock.png",
 | 
					 | 
				
			||||||
    fap_category="Tools",
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1.9 KiB  | 
@ -1,136 +0,0 @@
 | 
				
			|||||||
#include <furi.h>
 | 
					 | 
				
			||||||
#include <furi_hal.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <gui/gui.h>
 | 
					 | 
				
			||||||
#include <locale/locale.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    ClockEventTypeTick,
 | 
					 | 
				
			||||||
    ClockEventTypeKey,
 | 
					 | 
				
			||||||
} ClockEventType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    ClockEventType type;
 | 
					 | 
				
			||||||
    InputEvent input;
 | 
					 | 
				
			||||||
} ClockEvent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    FuriString* buffer;
 | 
					 | 
				
			||||||
    FuriHalRtcDateTime datetime;
 | 
					 | 
				
			||||||
    LocaleTimeFormat timeformat;
 | 
					 | 
				
			||||||
    LocaleDateFormat dateformat;
 | 
					 | 
				
			||||||
} ClockData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    FuriMutex* mutex;
 | 
					 | 
				
			||||||
    FuriMessageQueue* queue;
 | 
					 | 
				
			||||||
    ClockData* data;
 | 
					 | 
				
			||||||
} Clock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
 | 
					 | 
				
			||||||
    furi_assert(queue);
 | 
					 | 
				
			||||||
    ClockEvent event = {.type = ClockEventTypeKey, .input = *input_event};
 | 
					 | 
				
			||||||
    furi_message_queue_put(queue, &event, FuriWaitForever);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_render_callback(Canvas* canvas, void* ctx) {
 | 
					 | 
				
			||||||
    Clock* clock = ctx;
 | 
					 | 
				
			||||||
    if(furi_mutex_acquire(clock->mutex, 200) != FuriStatusOk) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ClockData* data = clock->data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    canvas_set_font(canvas, FontBigNumbers);
 | 
					 | 
				
			||||||
    locale_format_time(data->buffer, &data->datetime, data->timeformat, true);
 | 
					 | 
				
			||||||
    canvas_draw_str_aligned(
 | 
					 | 
				
			||||||
        canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Special case to cover missing glyphs in FontBigNumbers
 | 
					 | 
				
			||||||
    if(data->timeformat == LocaleTimeFormat12h) {
 | 
					 | 
				
			||||||
        size_t time_width = canvas_string_width(canvas, furi_string_get_cstr(data->buffer));
 | 
					 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					 | 
				
			||||||
        canvas_draw_str_aligned(
 | 
					 | 
				
			||||||
            canvas,
 | 
					 | 
				
			||||||
            64 + (time_width / 2) - 10,
 | 
					 | 
				
			||||||
            31,
 | 
					 | 
				
			||||||
            AlignLeft,
 | 
					 | 
				
			||||||
            AlignCenter,
 | 
					 | 
				
			||||||
            (data->datetime.hour > 11) ? "PM" : "AM");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    canvas_set_font(canvas, FontSecondary);
 | 
					 | 
				
			||||||
    locale_format_date(data->buffer, &data->datetime, data->dateformat, "/");
 | 
					 | 
				
			||||||
    canvas_draw_str_aligned(
 | 
					 | 
				
			||||||
        canvas, 64, 42, AlignCenter, AlignTop, furi_string_get_cstr(data->buffer));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_release(clock->mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void clock_tick(void* ctx) {
 | 
					 | 
				
			||||||
    furi_assert(ctx);
 | 
					 | 
				
			||||||
    FuriMessageQueue* queue = ctx;
 | 
					 | 
				
			||||||
    ClockEvent event = {.type = ClockEventTypeTick};
 | 
					 | 
				
			||||||
    // It's OK to loose this event if system overloaded
 | 
					 | 
				
			||||||
    furi_message_queue_put(queue, &event, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int32_t clock_app(void* p) {
 | 
					 | 
				
			||||||
    UNUSED(p);
 | 
					 | 
				
			||||||
    Clock* clock = malloc(sizeof(Clock));
 | 
					 | 
				
			||||||
    clock->data = malloc(sizeof(ClockData));
 | 
					 | 
				
			||||||
    clock->data->buffer = furi_string_alloc();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    clock->queue = furi_message_queue_alloc(8, sizeof(ClockEvent));
 | 
					 | 
				
			||||||
    clock->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_hal_rtc_get_datetime(&clock->data->datetime);
 | 
					 | 
				
			||||||
    clock->data->timeformat = locale_get_time_format();
 | 
					 | 
				
			||||||
    clock->data->dateformat = locale_get_date_format();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Set ViewPort callbacks
 | 
					 | 
				
			||||||
    ViewPort* view_port = view_port_alloc();
 | 
					 | 
				
			||||||
    view_port_draw_callback_set(view_port, clock_render_callback, clock);
 | 
					 | 
				
			||||||
    view_port_input_callback_set(view_port, clock_input_callback, clock->queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, clock->queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Open GUI and register view_port
 | 
					 | 
				
			||||||
    Gui* gui = furi_record_open(RECORD_GUI);
 | 
					 | 
				
			||||||
    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_timer_start(timer, 100);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Main loop
 | 
					 | 
				
			||||||
    ClockEvent event;
 | 
					 | 
				
			||||||
    for(bool processing = true; processing;) {
 | 
					 | 
				
			||||||
        furi_check(furi_message_queue_get(clock->queue, &event, FuriWaitForever) == FuriStatusOk);
 | 
					 | 
				
			||||||
        furi_mutex_acquire(clock->mutex, FuriWaitForever);
 | 
					 | 
				
			||||||
        if(event.type == ClockEventTypeKey) {
 | 
					 | 
				
			||||||
            if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
 | 
					 | 
				
			||||||
                processing = false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else if(event.type == ClockEventTypeTick) {
 | 
					 | 
				
			||||||
            furi_hal_rtc_get_datetime(&clock->data->datetime);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        furi_mutex_release(clock->mutex);
 | 
					 | 
				
			||||||
        view_port_update(view_port);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_timer_free(timer);
 | 
					 | 
				
			||||||
    view_port_enabled_set(view_port, false);
 | 
					 | 
				
			||||||
    gui_remove_view_port(gui, view_port);
 | 
					 | 
				
			||||||
    view_port_free(view_port);
 | 
					 | 
				
			||||||
    furi_record_close(RECORD_GUI);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_message_queue_free(clock->queue);
 | 
					 | 
				
			||||||
    furi_mutex_free(clock->mutex);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_string_free(clock->data->buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    free(clock->data);
 | 
					 | 
				
			||||||
    free(clock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
App(
 | 
					 | 
				
			||||||
    appid="music_player",
 | 
					 | 
				
			||||||
    name="Music Player",
 | 
					 | 
				
			||||||
    apptype=FlipperAppType.EXTERNAL,
 | 
					 | 
				
			||||||
    entry_point="music_player_app",
 | 
					 | 
				
			||||||
    requires=[
 | 
					 | 
				
			||||||
        "gui",
 | 
					 | 
				
			||||||
        "dialogs",
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    stack_size=2 * 1024,
 | 
					 | 
				
			||||||
    order=20,
 | 
					 | 
				
			||||||
    fap_icon="icons/music_10px.png",
 | 
					 | 
				
			||||||
    fap_category="Media",
 | 
					 | 
				
			||||||
    fap_icon_assets="icons",
 | 
					 | 
				
			||||||
    fap_libs=["music_worker"],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 142 B  | 
@ -1,372 +0,0 @@
 | 
				
			|||||||
#include <music_worker/music_worker.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <furi.h>
 | 
					 | 
				
			||||||
#include <furi_hal.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <music_player_icons.h>
 | 
					 | 
				
			||||||
#include <gui/gui.h>
 | 
					 | 
				
			||||||
#include <dialogs/dialogs.h>
 | 
					 | 
				
			||||||
#include <storage/storage.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define TAG "MusicPlayer"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MUSIC_PLAYER_APP_EXTENSION "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    uint8_t semitone_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE];
 | 
					 | 
				
			||||||
    uint8_t duration_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint8_t volume;
 | 
					 | 
				
			||||||
    uint8_t semitone;
 | 
					 | 
				
			||||||
    uint8_t dots;
 | 
					 | 
				
			||||||
    uint8_t duration;
 | 
					 | 
				
			||||||
    float position;
 | 
					 | 
				
			||||||
} MusicPlayerModel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    MusicPlayerModel* model;
 | 
					 | 
				
			||||||
    FuriMutex** model_mutex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FuriMessageQueue* input_queue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ViewPort* view_port;
 | 
					 | 
				
			||||||
    Gui* gui;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    MusicWorker* worker;
 | 
					 | 
				
			||||||
} MusicPlayer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const char* semitone_to_note(int8_t semitone) {
 | 
					 | 
				
			||||||
    switch(semitone) {
 | 
					 | 
				
			||||||
    case 0:
 | 
					 | 
				
			||||||
        return "C";
 | 
					 | 
				
			||||||
    case 1:
 | 
					 | 
				
			||||||
        return "C#";
 | 
					 | 
				
			||||||
    case 2:
 | 
					 | 
				
			||||||
        return "D";
 | 
					 | 
				
			||||||
    case 3:
 | 
					 | 
				
			||||||
        return "D#";
 | 
					 | 
				
			||||||
    case 4:
 | 
					 | 
				
			||||||
        return "E";
 | 
					 | 
				
			||||||
    case 5:
 | 
					 | 
				
			||||||
        return "F";
 | 
					 | 
				
			||||||
    case 6:
 | 
					 | 
				
			||||||
        return "F#";
 | 
					 | 
				
			||||||
    case 7:
 | 
					 | 
				
			||||||
        return "G";
 | 
					 | 
				
			||||||
    case 8:
 | 
					 | 
				
			||||||
        return "G#";
 | 
					 | 
				
			||||||
    case 9:
 | 
					 | 
				
			||||||
        return "A";
 | 
					 | 
				
			||||||
    case 10:
 | 
					 | 
				
			||||||
        return "A#";
 | 
					 | 
				
			||||||
    case 11:
 | 
					 | 
				
			||||||
        return "B";
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        return "--";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool is_white_note(uint8_t semitone, uint8_t id) {
 | 
					 | 
				
			||||||
    switch(semitone) {
 | 
					 | 
				
			||||||
    case 0:
 | 
					 | 
				
			||||||
        if(id == 0) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 2:
 | 
					 | 
				
			||||||
        if(id == 1) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 4:
 | 
					 | 
				
			||||||
        if(id == 2) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 5:
 | 
					 | 
				
			||||||
        if(id == 3) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 7:
 | 
					 | 
				
			||||||
        if(id == 4) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 9:
 | 
					 | 
				
			||||||
        if(id == 5) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 11:
 | 
					 | 
				
			||||||
        if(id == 6) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool is_black_note(uint8_t semitone, uint8_t id) {
 | 
					 | 
				
			||||||
    switch(semitone) {
 | 
					 | 
				
			||||||
    case 1:
 | 
					 | 
				
			||||||
        if(id == 0) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 3:
 | 
					 | 
				
			||||||
        if(id == 1) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 6:
 | 
					 | 
				
			||||||
        if(id == 3) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 8:
 | 
					 | 
				
			||||||
        if(id == 4) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case 10:
 | 
					 | 
				
			||||||
        if(id == 5) return true;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void render_callback(Canvas* canvas, void* ctx) {
 | 
					 | 
				
			||||||
    MusicPlayer* music_player = ctx;
 | 
					 | 
				
			||||||
    furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    canvas_clear(canvas);
 | 
					 | 
				
			||||||
    canvas_set_color(canvas, ColorBlack);
 | 
					 | 
				
			||||||
    canvas_set_font(canvas, FontPrimary);
 | 
					 | 
				
			||||||
    canvas_draw_str(canvas, 0, 12, "MusicPlayer");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint8_t x_pos = 0;
 | 
					 | 
				
			||||||
    uint8_t y_pos = 24;
 | 
					 | 
				
			||||||
    const uint8_t white_w = 10;
 | 
					 | 
				
			||||||
    const uint8_t white_h = 40;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const int8_t black_x = 6;
 | 
					 | 
				
			||||||
    const int8_t black_y = -5;
 | 
					 | 
				
			||||||
    const uint8_t black_w = 8;
 | 
					 | 
				
			||||||
    const uint8_t black_h = 32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // white keys
 | 
					 | 
				
			||||||
    for(size_t i = 0; i < 7; i++) {
 | 
					 | 
				
			||||||
        if(is_white_note(music_player->model->semitone, i)) {
 | 
					 | 
				
			||||||
            canvas_draw_box(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            canvas_draw_frame(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // black keys
 | 
					 | 
				
			||||||
    for(size_t i = 0; i < 7; i++) {
 | 
					 | 
				
			||||||
        if(i != 2 && i != 6) {
 | 
					 | 
				
			||||||
            canvas_set_color(canvas, ColorWhite);
 | 
					 | 
				
			||||||
            canvas_draw_box(
 | 
					 | 
				
			||||||
                canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
 | 
					 | 
				
			||||||
            canvas_set_color(canvas, ColorBlack);
 | 
					 | 
				
			||||||
            if(is_black_note(music_player->model->semitone, i)) {
 | 
					 | 
				
			||||||
                canvas_draw_box(
 | 
					 | 
				
			||||||
                    canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                canvas_draw_frame(
 | 
					 | 
				
			||||||
                    canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // volume view_port
 | 
					 | 
				
			||||||
    x_pos = 124;
 | 
					 | 
				
			||||||
    y_pos = 0;
 | 
					 | 
				
			||||||
    const uint8_t volume_h =
 | 
					 | 
				
			||||||
        (64 / (COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)) * music_player->model->volume;
 | 
					 | 
				
			||||||
    canvas_draw_frame(canvas, x_pos, y_pos, 4, 64);
 | 
					 | 
				
			||||||
    canvas_draw_box(canvas, x_pos, y_pos + (64 - volume_h), 4, volume_h);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // note stack view_port
 | 
					 | 
				
			||||||
    x_pos = 73;
 | 
					 | 
				
			||||||
    y_pos = 0; //-V1048
 | 
					 | 
				
			||||||
    canvas_set_color(canvas, ColorBlack);
 | 
					 | 
				
			||||||
    canvas_set_font(canvas, FontPrimary);
 | 
					 | 
				
			||||||
    canvas_draw_frame(canvas, x_pos, y_pos, 49, 64);
 | 
					 | 
				
			||||||
    canvas_draw_line(canvas, x_pos + 28, 0, x_pos + 28, 64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    char duration_text[16];
 | 
					 | 
				
			||||||
    for(uint8_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE; i++) {
 | 
					 | 
				
			||||||
        if(music_player->model->duration_history[i] == 0xFF) {
 | 
					 | 
				
			||||||
            snprintf(duration_text, 15, "--");
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            snprintf(duration_text, 15, "%d", music_player->model->duration_history[i]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if(i == 0) {
 | 
					 | 
				
			||||||
            canvas_draw_box(canvas, x_pos, y_pos + 48, 49, 16);
 | 
					 | 
				
			||||||
            canvas_set_color(canvas, ColorWhite);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            canvas_set_color(canvas, ColorBlack);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        canvas_draw_str(
 | 
					 | 
				
			||||||
            canvas,
 | 
					 | 
				
			||||||
            x_pos + 4,
 | 
					 | 
				
			||||||
            64 - 16 * i - 3,
 | 
					 | 
				
			||||||
            semitone_to_note(music_player->model->semitone_history[i]));
 | 
					 | 
				
			||||||
        canvas_draw_str(canvas, x_pos + 31, 64 - 16 * i - 3, duration_text);
 | 
					 | 
				
			||||||
        canvas_draw_line(canvas, x_pos, 64 - 16 * i, x_pos + 48, 64 - 16 * i);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_release(music_player->model_mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void input_callback(InputEvent* input_event, void* ctx) {
 | 
					 | 
				
			||||||
    MusicPlayer* music_player = ctx;
 | 
					 | 
				
			||||||
    if(input_event->type == InputTypeShort) {
 | 
					 | 
				
			||||||
        furi_message_queue_put(music_player->input_queue, input_event, 0);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void music_worker_callback(
 | 
					 | 
				
			||||||
    uint8_t semitone,
 | 
					 | 
				
			||||||
    uint8_t dots,
 | 
					 | 
				
			||||||
    uint8_t duration,
 | 
					 | 
				
			||||||
    float position,
 | 
					 | 
				
			||||||
    void* context) {
 | 
					 | 
				
			||||||
    MusicPlayer* music_player = context;
 | 
					 | 
				
			||||||
    furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for(size_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1; i++) {
 | 
					 | 
				
			||||||
        size_t r = MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1 - i;
 | 
					 | 
				
			||||||
        music_player->model->duration_history[r] = music_player->model->duration_history[r - 1];
 | 
					 | 
				
			||||||
        music_player->model->semitone_history[r] = music_player->model->semitone_history[r - 1];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    semitone = (semitone == 0xFF) ? 0xFF : semitone % 12;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    music_player->model->semitone = semitone;
 | 
					 | 
				
			||||||
    music_player->model->dots = dots;
 | 
					 | 
				
			||||||
    music_player->model->duration = duration;
 | 
					 | 
				
			||||||
    music_player->model->position = position;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    music_player->model->semitone_history[0] = semitone;
 | 
					 | 
				
			||||||
    music_player->model->duration_history[0] = duration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_release(music_player->model_mutex);
 | 
					 | 
				
			||||||
    view_port_update(music_player->view_port);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void music_player_clear(MusicPlayer* instance) {
 | 
					 | 
				
			||||||
    memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
 | 
					 | 
				
			||||||
    memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
 | 
					 | 
				
			||||||
    music_worker_clear(instance->worker);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MusicPlayer* music_player_alloc() {
 | 
					 | 
				
			||||||
    MusicPlayer* instance = malloc(sizeof(MusicPlayer));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    instance->model = malloc(sizeof(MusicPlayerModel));
 | 
					 | 
				
			||||||
    instance->model->volume = 3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    instance->worker = music_worker_alloc();
 | 
					 | 
				
			||||||
    music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]);
 | 
					 | 
				
			||||||
    music_worker_set_callback(instance->worker, music_worker_callback, instance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    music_player_clear(instance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    instance->view_port = view_port_alloc();
 | 
					 | 
				
			||||||
    view_port_draw_callback_set(instance->view_port, render_callback, instance);
 | 
					 | 
				
			||||||
    view_port_input_callback_set(instance->view_port, input_callback, instance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Open GUI and register view_port
 | 
					 | 
				
			||||||
    instance->gui = furi_record_open(RECORD_GUI);
 | 
					 | 
				
			||||||
    gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return instance;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void music_player_free(MusicPlayer* instance) {
 | 
					 | 
				
			||||||
    gui_remove_view_port(instance->gui, instance->view_port);
 | 
					 | 
				
			||||||
    furi_record_close(RECORD_GUI);
 | 
					 | 
				
			||||||
    view_port_free(instance->view_port);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    music_worker_free(instance->worker);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_message_queue_free(instance->input_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_free(instance->model_mutex);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    free(instance->model);
 | 
					 | 
				
			||||||
    free(instance);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int32_t music_player_app(void* p) {
 | 
					 | 
				
			||||||
    MusicPlayer* music_player = music_player_alloc();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FuriString* file_path;
 | 
					 | 
				
			||||||
    file_path = furi_string_alloc();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
        if(p && strlen(p)) {
 | 
					 | 
				
			||||||
            furi_string_set(file_path, (const char*)p);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            Storage* storage = furi_record_open(RECORD_STORAGE);
 | 
					 | 
				
			||||||
            storage_common_migrate(
 | 
					 | 
				
			||||||
                storage, EXT_PATH("music_player"), STORAGE_APP_DATA_PATH_PREFIX);
 | 
					 | 
				
			||||||
            furi_record_close(RECORD_STORAGE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            furi_string_set(file_path, STORAGE_APP_DATA_PATH_PREFIX);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            DialogsFileBrowserOptions browser_options;
 | 
					 | 
				
			||||||
            dialog_file_browser_set_basic_options(
 | 
					 | 
				
			||||||
                &browser_options, MUSIC_PLAYER_APP_EXTENSION, &I_music_10px);
 | 
					 | 
				
			||||||
            browser_options.hide_ext = false;
 | 
					 | 
				
			||||||
            browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
 | 
					 | 
				
			||||||
            bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            furi_record_close(RECORD_DIALOGS);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(!res) {
 | 
					 | 
				
			||||||
                FURI_LOG_E(TAG, "No file selected");
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) {
 | 
					 | 
				
			||||||
            FURI_LOG_E(TAG, "Unable to load file");
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        music_worker_start(music_player->worker);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InputEvent input;
 | 
					 | 
				
			||||||
        while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) ==
 | 
					 | 
				
			||||||
              FuriStatusOk) {
 | 
					 | 
				
			||||||
            furi_check(
 | 
					 | 
				
			||||||
                furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(input.key == InputKeyBack) {
 | 
					 | 
				
			||||||
                furi_mutex_release(music_player->model_mutex);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            } else if(input.key == InputKeyUp) {
 | 
					 | 
				
			||||||
                if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)
 | 
					 | 
				
			||||||
                    music_player->model->volume++;
 | 
					 | 
				
			||||||
                music_worker_set_volume(
 | 
					 | 
				
			||||||
                    music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
 | 
					 | 
				
			||||||
            } else if(input.key == InputKeyDown) {
 | 
					 | 
				
			||||||
                if(music_player->model->volume > 0) music_player->model->volume--;
 | 
					 | 
				
			||||||
                music_worker_set_volume(
 | 
					 | 
				
			||||||
                    music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            furi_mutex_release(music_player->model_mutex);
 | 
					 | 
				
			||||||
            view_port_update(music_player->view_port);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        music_worker_stop(music_player->worker);
 | 
					 | 
				
			||||||
        if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg
 | 
					 | 
				
			||||||
        music_player_clear(music_player);
 | 
					 | 
				
			||||||
    } while(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_string_free(file_path);
 | 
					 | 
				
			||||||
    music_player_free(music_player);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
App(
 | 
					 | 
				
			||||||
    appid="snake_game",
 | 
					 | 
				
			||||||
    name="Snake Game",
 | 
					 | 
				
			||||||
    apptype=FlipperAppType.EXTERNAL,
 | 
					 | 
				
			||||||
    entry_point="snake_game_app",
 | 
					 | 
				
			||||||
    requires=["gui"],
 | 
					 | 
				
			||||||
    stack_size=1 * 1024,
 | 
					 | 
				
			||||||
    order=30,
 | 
					 | 
				
			||||||
    fap_icon="snake_10px.png",
 | 
					 | 
				
			||||||
    fap_category="Games",
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 158 B  | 
@ -1,434 +0,0 @@
 | 
				
			|||||||
#include <furi.h>
 | 
					 | 
				
			||||||
#include <gui/gui.h>
 | 
					 | 
				
			||||||
#include <input/input.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
#include <dolphin/dolphin.h>
 | 
					 | 
				
			||||||
#include <notification/notification.h>
 | 
					 | 
				
			||||||
#include <notification/notification_messages.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    //    +-----x
 | 
					 | 
				
			||||||
    //    |
 | 
					 | 
				
			||||||
    //    |
 | 
					 | 
				
			||||||
    //    y
 | 
					 | 
				
			||||||
    uint8_t x;
 | 
					 | 
				
			||||||
    uint8_t y;
 | 
					 | 
				
			||||||
} Point;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    GameStateLife,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto
 | 
					 | 
				
			||||||
    // Armanto: While testing the early versions of the game, I noticed it was hard
 | 
					 | 
				
			||||||
    // to control the snake upon getting close to and edge but not crashing — especially
 | 
					 | 
				
			||||||
    // in the highest speed levels. I wanted the highest level to be as fast as I could
 | 
					 | 
				
			||||||
    // possibly make the device "run," but on the other hand, I wanted to be friendly
 | 
					 | 
				
			||||||
    // and help the player manage that level. Otherwise it might not be fun to play. So
 | 
					 | 
				
			||||||
    // I implemented a little delay. A few milliseconds of extra time right before
 | 
					 | 
				
			||||||
    // the player crashes, during which she can still change the directions. And if
 | 
					 | 
				
			||||||
    // she does, the game continues.
 | 
					 | 
				
			||||||
    GameStateLastChance,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    GameStateGameOver,
 | 
					 | 
				
			||||||
} GameState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Note: do not change without purpose. Current values are used in smart
 | 
					 | 
				
			||||||
// orthogonality calculation in `snake_game_get_turn_snake`.
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    DirectionUp,
 | 
					 | 
				
			||||||
    DirectionRight,
 | 
					 | 
				
			||||||
    DirectionDown,
 | 
					 | 
				
			||||||
    DirectionLeft,
 | 
					 | 
				
			||||||
} Direction;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_SNAKE_LEN 253
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    Point points[MAX_SNAKE_LEN];
 | 
					 | 
				
			||||||
    uint16_t len;
 | 
					 | 
				
			||||||
    Direction currentMovement;
 | 
					 | 
				
			||||||
    Direction nextMovement; // if backward of currentMovement, ignore
 | 
					 | 
				
			||||||
    Point fruit;
 | 
					 | 
				
			||||||
    GameState state;
 | 
					 | 
				
			||||||
    FuriMutex* mutex;
 | 
					 | 
				
			||||||
} SnakeState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef enum {
 | 
					 | 
				
			||||||
    EventTypeTick,
 | 
					 | 
				
			||||||
    EventTypeKey,
 | 
					 | 
				
			||||||
} EventType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    EventType type;
 | 
					 | 
				
			||||||
    InputEvent input;
 | 
					 | 
				
			||||||
} SnakeEvent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const NotificationSequence sequence_fail = {
 | 
					 | 
				
			||||||
    &message_vibro_on,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &message_note_ds4,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
    &message_sound_off,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &message_note_ds4,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
    &message_sound_off,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &message_note_ds4,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
    &message_sound_off,
 | 
					 | 
				
			||||||
    &message_delay_10,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    &message_vibro_off,
 | 
					 | 
				
			||||||
    NULL,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const NotificationSequence sequence_eat = {
 | 
					 | 
				
			||||||
    &message_note_c7,
 | 
					 | 
				
			||||||
    &message_delay_50,
 | 
					 | 
				
			||||||
    &message_sound_off,
 | 
					 | 
				
			||||||
    NULL,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
 | 
					 | 
				
			||||||
    furi_assert(ctx);
 | 
					 | 
				
			||||||
    const SnakeState* snake_state = ctx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_acquire(snake_state->mutex, FuriWaitForever);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Frame
 | 
					 | 
				
			||||||
    canvas_draw_frame(canvas, 0, 0, 128, 64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Fruit
 | 
					 | 
				
			||||||
    Point f = snake_state->fruit;
 | 
					 | 
				
			||||||
    f.x = f.x * 4 + 1;
 | 
					 | 
				
			||||||
    f.y = f.y * 4 + 1;
 | 
					 | 
				
			||||||
    canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Snake
 | 
					 | 
				
			||||||
    for(uint16_t i = 0; i < snake_state->len; i++) {
 | 
					 | 
				
			||||||
        Point p = snake_state->points[i];
 | 
					 | 
				
			||||||
        p.x = p.x * 4 + 2;
 | 
					 | 
				
			||||||
        p.y = p.y * 4 + 2;
 | 
					 | 
				
			||||||
        canvas_draw_box(canvas, p.x, p.y, 4, 4);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Game Over banner
 | 
					 | 
				
			||||||
    if(snake_state->state == GameStateGameOver) {
 | 
					 | 
				
			||||||
        // Screen is 128x64 px
 | 
					 | 
				
			||||||
        canvas_set_color(canvas, ColorWhite);
 | 
					 | 
				
			||||||
        canvas_draw_box(canvas, 34, 20, 62, 24);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        canvas_set_color(canvas, ColorBlack);
 | 
					 | 
				
			||||||
        canvas_draw_frame(canvas, 34, 20, 62, 24);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        canvas_set_font(canvas, FontPrimary);
 | 
					 | 
				
			||||||
        canvas_draw_str(canvas, 37, 31, "Game Over");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        canvas_set_font(canvas, FontSecondary);
 | 
					 | 
				
			||||||
        char buffer[12];
 | 
					 | 
				
			||||||
        snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U);
 | 
					 | 
				
			||||||
        canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_mutex_release(snake_state->mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
 | 
					 | 
				
			||||||
    furi_assert(event_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SnakeEvent event = {.type = EventTypeKey, .input = *input_event};
 | 
					 | 
				
			||||||
    furi_message_queue_put(event_queue, &event, FuriWaitForever);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) {
 | 
					 | 
				
			||||||
    furi_assert(event_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SnakeEvent event = {.type = EventTypeTick};
 | 
					 | 
				
			||||||
    furi_message_queue_put(event_queue, &event, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)); //-V1086
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_state->len = 7;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_state->currentMovement = DirectionRight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_state->nextMovement = DirectionRight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Point f = {18, 6};
 | 
					 | 
				
			||||||
    snake_state->fruit = f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_state->state = GameStateLife;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static Point snake_game_get_new_fruit(SnakeState const* const snake_state) {
 | 
					 | 
				
			||||||
    // 1 bit for each point on the playing field where the snake can turn
 | 
					 | 
				
			||||||
    // and where the fruit can appear
 | 
					 | 
				
			||||||
    uint16_t buffer[8];
 | 
					 | 
				
			||||||
    memset(buffer, 0, sizeof(buffer));
 | 
					 | 
				
			||||||
    uint8_t empty = 8 * 16;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for(uint16_t i = 0; i < snake_state->len; i++) {
 | 
					 | 
				
			||||||
        Point p = snake_state->points[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if(p.x % 2 != 0 || p.y % 2 != 0) {
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        p.x /= 2;
 | 
					 | 
				
			||||||
        p.y /= 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        buffer[p.y] |= 1 << p.x;
 | 
					 | 
				
			||||||
        empty--;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // Bit set if snake use that playing field
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint16_t newFruit = rand() % empty;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Skip random number of _empty_ fields
 | 
					 | 
				
			||||||
    for(uint8_t y = 0; y < 8; y++) {
 | 
					 | 
				
			||||||
        for(uint16_t x = 0, mask = 1; x < 16; x += 1, mask <<= 1) {
 | 
					 | 
				
			||||||
            if((buffer[y] & mask) == 0) {
 | 
					 | 
				
			||||||
                if(newFruit == 0) {
 | 
					 | 
				
			||||||
                    Point p = {
 | 
					 | 
				
			||||||
                        .x = x * 2,
 | 
					 | 
				
			||||||
                        .y = y * 2,
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    return p;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                newFruit--;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // We will never be here
 | 
					 | 
				
			||||||
    Point p = {0, 0};
 | 
					 | 
				
			||||||
    return p;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool snake_game_collision_with_frame(Point const next_step) {
 | 
					 | 
				
			||||||
    // if x == 0 && currentMovement == left then x - 1 == 255 ,
 | 
					 | 
				
			||||||
    // so check only x > right border
 | 
					 | 
				
			||||||
    return next_step.x > 30 || next_step.y > 14;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool
 | 
					 | 
				
			||||||
    snake_game_collision_with_tail(SnakeState const* const snake_state, Point const next_step) {
 | 
					 | 
				
			||||||
    for(uint16_t i = 0; i < snake_state->len; i++) {
 | 
					 | 
				
			||||||
        Point p = snake_state->points[i];
 | 
					 | 
				
			||||||
        if(p.x == next_step.x && p.y == next_step.y) {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static Direction snake_game_get_turn_snake(SnakeState const* const snake_state) {
 | 
					 | 
				
			||||||
    // Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality.
 | 
					 | 
				
			||||||
    bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1;
 | 
					 | 
				
			||||||
    return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static Point snake_game_get_next_step(SnakeState const* const snake_state) {
 | 
					 | 
				
			||||||
    Point next_step = snake_state->points[0];
 | 
					 | 
				
			||||||
    switch(snake_state->currentMovement) {
 | 
					 | 
				
			||||||
    // +-----x
 | 
					 | 
				
			||||||
    // |
 | 
					 | 
				
			||||||
    // |
 | 
					 | 
				
			||||||
    // y
 | 
					 | 
				
			||||||
    case DirectionUp:
 | 
					 | 
				
			||||||
        next_step.y--;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case DirectionRight:
 | 
					 | 
				
			||||||
        next_step.x++;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case DirectionDown:
 | 
					 | 
				
			||||||
        next_step.y++;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    case DirectionLeft:
 | 
					 | 
				
			||||||
        next_step.x--;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return next_step;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void snake_game_move_snake(SnakeState* const snake_state, Point const next_step) {
 | 
					 | 
				
			||||||
    memmove(snake_state->points + 1, snake_state->points, snake_state->len * sizeof(Point));
 | 
					 | 
				
			||||||
    snake_state->points[0] = next_step;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
    snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) {
 | 
					 | 
				
			||||||
    if(snake_state->state == GameStateGameOver) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0);
 | 
					 | 
				
			||||||
    if(can_turn) {
 | 
					 | 
				
			||||||
        snake_state->currentMovement = snake_game_get_turn_snake(snake_state);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Point next_step = snake_game_get_next_step(snake_state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool crush = snake_game_collision_with_frame(next_step);
 | 
					 | 
				
			||||||
    if(crush) {
 | 
					 | 
				
			||||||
        if(snake_state->state == GameStateLife) {
 | 
					 | 
				
			||||||
            snake_state->state = GameStateLastChance;
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        } else if(snake_state->state == GameStateLastChance) {
 | 
					 | 
				
			||||||
            snake_state->state = GameStateGameOver;
 | 
					 | 
				
			||||||
            notification_message_block(notification, &sequence_fail);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        if(snake_state->state == GameStateLastChance) {
 | 
					 | 
				
			||||||
            snake_state->state = GameStateLife;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    crush = snake_game_collision_with_tail(snake_state, next_step);
 | 
					 | 
				
			||||||
    if(crush) {
 | 
					 | 
				
			||||||
        snake_state->state = GameStateGameOver;
 | 
					 | 
				
			||||||
        notification_message_block(notification, &sequence_fail);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y);
 | 
					 | 
				
			||||||
    if(eatFruit) {
 | 
					 | 
				
			||||||
        snake_state->len++;
 | 
					 | 
				
			||||||
        if(snake_state->len >= MAX_SNAKE_LEN) {
 | 
					 | 
				
			||||||
            snake_state->state = GameStateGameOver;
 | 
					 | 
				
			||||||
            notification_message_block(notification, &sequence_fail);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_game_move_snake(snake_state, next_step);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(eatFruit) {
 | 
					 | 
				
			||||||
        snake_state->fruit = snake_game_get_new_fruit(snake_state);
 | 
					 | 
				
			||||||
        notification_message(notification, &sequence_eat);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int32_t snake_game_app(void* p) {
 | 
					 | 
				
			||||||
    UNUSED(p);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SnakeState* snake_state = malloc(sizeof(SnakeState));
 | 
					 | 
				
			||||||
    snake_game_init_game(snake_state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(!snake_state->mutex) {
 | 
					 | 
				
			||||||
        FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
 | 
					 | 
				
			||||||
        free(snake_state);
 | 
					 | 
				
			||||||
        return 255;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ViewPort* view_port = view_port_alloc();
 | 
					 | 
				
			||||||
    view_port_draw_callback_set(view_port, snake_game_render_callback, snake_state);
 | 
					 | 
				
			||||||
    view_port_input_callback_set(view_port, snake_game_input_callback, event_queue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FuriTimer* timer =
 | 
					 | 
				
			||||||
        furi_timer_alloc(snake_game_update_timer_callback, FuriTimerTypePeriodic, event_queue);
 | 
					 | 
				
			||||||
    furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Open GUI and register view_port
 | 
					 | 
				
			||||||
    Gui* gui = furi_record_open(RECORD_GUI);
 | 
					 | 
				
			||||||
    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 | 
					 | 
				
			||||||
    NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    notification_message_block(notification, &sequence_display_backlight_enforce_on);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dolphin_deed(DolphinDeedPluginGameStart);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SnakeEvent event;
 | 
					 | 
				
			||||||
    for(bool processing = true; processing;) {
 | 
					 | 
				
			||||||
        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        furi_mutex_acquire(snake_state->mutex, FuriWaitForever);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if(event_status == FuriStatusOk) {
 | 
					 | 
				
			||||||
            // press events
 | 
					 | 
				
			||||||
            if(event.type == EventTypeKey) {
 | 
					 | 
				
			||||||
                if(event.input.type == InputTypePress) {
 | 
					 | 
				
			||||||
                    switch(event.input.key) {
 | 
					 | 
				
			||||||
                    case InputKeyUp:
 | 
					 | 
				
			||||||
                        snake_state->nextMovement = DirectionUp;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case InputKeyDown:
 | 
					 | 
				
			||||||
                        snake_state->nextMovement = DirectionDown;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case InputKeyRight:
 | 
					 | 
				
			||||||
                        snake_state->nextMovement = DirectionRight;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case InputKeyLeft:
 | 
					 | 
				
			||||||
                        snake_state->nextMovement = DirectionLeft;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case InputKeyOk:
 | 
					 | 
				
			||||||
                        if(snake_state->state == GameStateGameOver) {
 | 
					 | 
				
			||||||
                            snake_game_init_game(snake_state);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case InputKeyBack:
 | 
					 | 
				
			||||||
                        processing = false;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    default:
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else if(event.type == EventTypeTick) {
 | 
					 | 
				
			||||||
                snake_game_process_game_step(snake_state, notification);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // event timeout
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        view_port_update(view_port);
 | 
					 | 
				
			||||||
        furi_mutex_release(snake_state->mutex);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Return backlight to normal state
 | 
					 | 
				
			||||||
    notification_message(notification, &sequence_display_backlight_enforce_auto);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    furi_timer_free(timer);
 | 
					 | 
				
			||||||
    view_port_enabled_set(view_port, false);
 | 
					 | 
				
			||||||
    gui_remove_view_port(gui, view_port);
 | 
					 | 
				
			||||||
    furi_record_close(RECORD_GUI);
 | 
					 | 
				
			||||||
    furi_record_close(RECORD_NOTIFICATION);
 | 
					 | 
				
			||||||
    view_port_free(view_port);
 | 
					 | 
				
			||||||
    furi_message_queue_free(event_queue);
 | 
					 | 
				
			||||||
    furi_mutex_free(snake_state->mutex);
 | 
					 | 
				
			||||||
    free(snake_state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Screen is 128x64 px
 | 
					 | 
				
			||||||
// (4 + 4) * 16 - 4 + 2 + 2border == 128
 | 
					 | 
				
			||||||
// (4 + 4) * 8 - 4 + 2 + 2border == 64
 | 
					 | 
				
			||||||
// Game field from point{x:  0, y: 0} to point{x: 30, y: 14}.
 | 
					 | 
				
			||||||
// The snake turns only in even cells - intersections.
 | 
					 | 
				
			||||||
// ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪   ▪ ╎
 | 
					 | 
				
			||||||
// ╎ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ╎
 | 
					 | 
				
			||||||
// └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
 | 
					 | 
				
			||||||
@ -1,6 +0,0 @@
 | 
				
			|||||||
Filetype: Flipper Music Format
 | 
					 | 
				
			||||||
Version: 0
 | 
					 | 
				
			||||||
BPM: 130
 | 
					 | 
				
			||||||
Duration: 8
 | 
					 | 
				
			||||||
Octave: 5
 | 
					 | 
				
			||||||
Notes: E6, P, E, B, 4P, E, A, G, A, E, B, P, G, A, D6, 4P, D, B, 4P, D, A, G, A, D, F#, P, G, A, D6, 4P, F#, B, 4P, F#, D6, C6, B, F#, A, P, G, F#, E, P, C, E, B, B4, C, D, D6, C6, B, F#, A, P, G, A, E6, 4P, E, B, 4P, E, A, G, A, E, B, P, G, A, D6, 4P, D, B, 4P, D, A, G, A, D, F#, P, G, A, D6, 4P, F#, B, 4P, F#, D6, C6, B, F#, A, P, G, F#, E, P, C, E, B, B4, C, D, D6, C6, B, F#, A, P, G, A, E6
 | 
					 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# key1/Kc from PicoPass 2k documentation
 | 
					 | 
				
			||||||
7665544332211000
 | 
					 | 
				
			||||||
# SAGEM
 | 
					 | 
				
			||||||
0123456789ABCDEF
 | 
					 | 
				
			||||||
# PicoPass Default Exchange Key
 | 
					 | 
				
			||||||
5CBCF1DA45D5FB4F
 | 
					 | 
				
			||||||
# From HID multiclassSE reader
 | 
					 | 
				
			||||||
31ad7ebd2f282168
 | 
					 | 
				
			||||||
# From pastebin: https://pastebin.com/uHqpjiuU
 | 
					 | 
				
			||||||
6EFD46EFCBB3C875
 | 
					 | 
				
			||||||
E033CA419AEE43F9
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# default picopass KD / Page 0 / Book 1
 | 
					 | 
				
			||||||
FDCB5A52EA8F3090
 | 
					 | 
				
			||||||
237FF9079863DF44
 | 
					 | 
				
			||||||
5ADC25FB27181D32
 | 
					 | 
				
			||||||
83B881F2936B2E49
 | 
					 | 
				
			||||||
43644E61EE866BA5
 | 
					 | 
				
			||||||
897034143D016080
 | 
					 | 
				
			||||||
82D17B44C0122963
 | 
					 | 
				
			||||||
4895CA7DE65E2025
 | 
					 | 
				
			||||||
DADAD4C57BE271B7
 | 
					 | 
				
			||||||
E41E9EDEF5719ABF
 | 
					 | 
				
			||||||
293D275EC3AF9C7F
 | 
					 | 
				
			||||||
C3C169251B8A70FB
 | 
					 | 
				
			||||||
F41DAF58B20C8B91
 | 
					 | 
				
			||||||
28877A609EC0DD2B
 | 
					 | 
				
			||||||
66584C91EE80D5E5
 | 
					 | 
				
			||||||
C1B74D7478053AE2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# default iCLASS RFIDeas
 | 
					 | 
				
			||||||
6B65797374726B72
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# CTF key
 | 
					 | 
				
			||||||
5C100DF7042EAE64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# iCopy-X DRM key (iCE product)
 | 
					 | 
				
			||||||
2020666666668888
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# AA1
 | 
					 | 
				
			||||||
AEA684A6DAB23278
 | 
					 | 
				
			||||||
# key1/Kc from PicoPass 2k documentation
 | 
					 | 
				
			||||||
7665544332211000
 | 
					 | 
				
			||||||
# SAGEM
 | 
					 | 
				
			||||||
0123456789ABCDEF
 | 
					 | 
				
			||||||
# from loclass demo file.
 | 
					 | 
				
			||||||
5b7c62c491c11b39
 | 
					 | 
				
			||||||
# Kd from PicoPass 2k documentation
 | 
					 | 
				
			||||||
F0E1D2C3B4A59687
 | 
					 | 
				
			||||||
# PicoPass Default Exchange Key
 | 
					 | 
				
			||||||
5CBCF1DA45D5FB4F
 | 
					 | 
				
			||||||
# From HID multiclassSE reader
 | 
					 | 
				
			||||||
31ad7ebd2f282168
 | 
					 | 
				
			||||||
# From pastebin: https://pastebin.com/uHqpjiuU
 | 
					 | 
				
			||||||
6EFD46EFCBB3C875
 | 
					 | 
				
			||||||
E033CA419AEE43F9
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# iCopy-x DRM keys
 | 
					 | 
				
			||||||
# iCL tags
 | 
					 | 
				
			||||||
2020666666668888
 | 
					 | 
				
			||||||
# iCS tags reversed from the SOs
 | 
					 | 
				
			||||||
6666202066668888
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# default picopass KD / Page 0 / Book 1
 | 
					 | 
				
			||||||
FDCB5A52EA8F3090
 | 
					 | 
				
			||||||
237FF9079863DF44
 | 
					 | 
				
			||||||
5ADC25FB27181D32
 | 
					 | 
				
			||||||
83B881F2936B2E49
 | 
					 | 
				
			||||||
43644E61EE866BA5
 | 
					 | 
				
			||||||
897034143D016080
 | 
					 | 
				
			||||||
82D17B44C0122963
 | 
					 | 
				
			||||||
4895CA7DE65E2025
 | 
					 | 
				
			||||||
DADAD4C57BE271B7
 | 
					 | 
				
			||||||
E41E9EDEF5719ABF
 | 
					 | 
				
			||||||
293D275EC3AF9C7F
 | 
					 | 
				
			||||||
C3C169251B8A70FB
 | 
					 | 
				
			||||||
F41DAF58B20C8B91
 | 
					 | 
				
			||||||
28877A609EC0DD2B
 | 
					 | 
				
			||||||
66584C91EE80D5E5
 | 
					 | 
				
			||||||
C1B74D7478053AE2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# default iCLASS RFIDeas
 | 
					 | 
				
			||||||
6B65797374726B72
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user