Parcourir la source

Add barcode_gen from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: barcode_gen
git-subtree-mainline: a691d43ed1b4605e200daa86f91f9fbf78cb47d7
git-subtree-split: e038061a194fa0a51ebdf267286a42e7ca190c66
Willy-JL il y a 2 ans
Parent
commit
56d885b600
34 fichiers modifiés avec 2926 ajouts et 0 suppressions
  1. 1 0
      barcode_gen/.gitsubtree
  2. 22 0
      barcode_gen/LICENSE
  3. 88 0
      barcode_gen/README.md
  4. 16 0
      barcode_gen/application.fam
  5. 369 0
      barcode_gen/barcode_app.c
  6. 88 0
      barcode_gen/barcode_app.h
  7. 22 0
      barcode_gen/barcode_encoding_files/codabar_encodings.txt
  8. 202 0
      barcode_gen/barcode_encoding_files/code128_encodings.txt
  9. 106 0
      barcode_gen/barcode_encoding_files/code128c_encodings.txt
  10. 44 0
      barcode_gen/barcode_encoding_files/code39_encodings.txt
  11. 147 0
      barcode_gen/barcode_utils.c
  12. 55 0
      barcode_gen/barcode_utils.h
  13. 532 0
      barcode_gen/barcode_validator.c
  14. 15 0
      barcode_gen/barcode_validator.h
  15. 52 0
      barcode_gen/encodings.c
  16. 6 0
      barcode_gen/encodings.h
  17. BIN
      barcode_gen/images/barcode_10.png
  18. BIN
      barcode_gen/img/1.png
  19. BIN
      barcode_gen/img/2.png
  20. BIN
      barcode_gen/img/3.png
  21. BIN
      barcode_gen/img/Codabar Data Example.png
  22. BIN
      barcode_gen/img/Creating Barcode.png
  23. BIN
      barcode_gen/img/Flipper Barcode.png
  24. BIN
      barcode_gen/img/Flipper Box Barcode.png
  25. BIN
      barcode_gen/screenshots/Codabar Data Example.png
  26. BIN
      barcode_gen/screenshots/Creating Barcode.png
  27. BIN
      barcode_gen/screenshots/Flipper Barcode.png
  28. BIN
      barcode_gen/screenshots/Flipper Box Barcode.png
  29. 510 0
      barcode_gen/views/barcode_view.c
  30. 23 0
      barcode_gen/views/barcode_view.h
  31. 494 0
      barcode_gen/views/create_view.c
  32. 46 0
      barcode_gen/views/create_view.h
  33. 66 0
      barcode_gen/views/message_view.c
  34. 22 0
      barcode_gen/views/message_view.h

+ 1 - 0
barcode_gen/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev base_pack/barcode_gen

+ 22 - 0
barcode_gen/LICENSE

@@ -0,0 +1,22 @@
+
+MIT License
+
+Copyright (c) 2023 Alan Tsui
+
+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.

+ 88 - 0
barcode_gen/README.md

@@ -0,0 +1,88 @@
+<p align="center">
+	<h1 align="center">Barcode Generator</h1>  
+  <p align="center">
+
+A barcode generator for the Flipper Zero that supports **UPC-A**, **EAN-8**, **EAN-13**, **Code-39**, **Codabar**, and **Code-128**[1]
+</p>
+
+Note: Barcode save locations have been moved from `/barcodes` to `/apps_data/barcodes`
+
+## Table of Contents
+- [Table of Contents](#table-of-contents)
+- [Installing](#installing)
+- [Building](#building)
+- [Usage](#usage)
+  - [Creating a barcode](#creating-a-barcode)
+  - [Editing a barcode](#editing-a-barcode)
+  - [Deleting a barcode](#deleting-a-barcode)
+  - [Viewing a barcode](#viewing-a-barcode)
+- [Screenshots](#screenshots)
+- [Credits](#credits)
+
+
+## Installing
+1) Download the `.zip` file from the release section
+2) Extract/unzip the `.zip` file onto your computer
+3) Open qFlipper and go to the file manager
+4) Navigate to the `apps` folder
+5) Drag & drop the `.fap` file into the `apps` folder
+6) Navigate back to the root folder of the SD card and create the folder `apps_data`, if not already there
+7) Navigate into `apps_data` and create another folder called `barcode_data`
+8) Navigate into `barcode_data`
+9) Drag & drop the encoding txts (`code39_encodings.txt`, `code128_encodings.txt` & `codabar_encodings.txt`) into the `barcode_data` folder
+
+## Building
+1) Clone the [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) repository or a firmware of your choice
+2) Clone this repository and put it in the `applications_user` folder
+3) Build this app by using the command `./fbt fap_Barcode_App`
+4) Copy the `.fap` from `build\f7-firmware-D\.extapps\Barcode_App.fap` to `apps\Misc` using the qFlipper app
+5) While still in the qFlipper app, navigate to the root folder of the SD card and create the folder `apps_data`, if not already there
+6) Navigate into `apps_data` and create another folder called `barcode_data`
+7) Navigate into `barcode_data`
+8) Drag & drop the encoding txts (`code39_encodings.txt`, `code128_encodings.txt` & `codabar_encodings.txt`) from the `encoding_tables` folder in this repository into the `barcode_data` folder
+
+## Usage
+
+### Creating a barcode
+1) To create a barcode click on `Create Barcode`
+2) Next select your type using the left and right arrows
+3) Enter your filename and then your barcode data
+4) Click save
+
+**Note**: For Codabar barcodes, you must manually add the start and stop codes to the barcode data
+Start/Stop codes can be A, B, C, or D
+For example, if you wanted to represent `1234` as a barcode you will need to enter something like `A1234A`. (You can replace the letters A with either A, B, C, or D)
+
+![Codabar Data Example](screenshots/Codabar%20Data%20Example.png "Codabar Data Example")
+
+### Editing a barcode
+1) To edit a barcode click on `Edit Barcode`
+2) Next select the barcode file you want to edit
+3) Edit the type, name, or data
+4) Click save
+
+### Deleting a barcode
+1) To delete a barcode click on `Edit Barcode`
+2) Next select the barcode file you want to delete
+3) Scroll all the way to the bottom
+4) Click delete
+
+### Viewing a barcode
+1) To view a barcode click on `Load Barcode`
+2) Next select the barcode file you want to view
+
+## Screenshots
+![Barcode Create Screen](screenshots/Creating%20Barcode.png "Barcode Create Screen")
+
+![Flipper Code-128 Barcode](screenshots/Flipper%20Barcode.png "Flipper Code-128 Barcode")
+
+![Flipper Box EAN-13 Barcode](screenshots/Flipper%20Box%20Barcode.png "Flipper Box EAN-13 Barcode")
+
+## Credits
+
+- [Kingal1337](https://github.com/Kingal1337) - Developer
+- [Z0wl](https://github.com/Z0wl) - Added Code128-C Support
+- [@teeebor](https://github.com/teeebor) - Menu Code Snippet
+
+
+[1] - supports Set B (only the characters from 0-94). Also supports Set C

+ 16 - 0
barcode_gen/application.fam

@@ -0,0 +1,16 @@
+App(
+    appid="barcode_app",
+    name="Barcode App",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="barcode_main",
+    requires=["gui", "storage"],
+    stack_size=2 * 1024,
+    fap_category="Tools",
+    fap_icon="images/barcode_10.png",
+    fap_icon_assets="images",
+    fap_file_assets="barcode_encoding_files",
+    fap_author="@Kingal1337",
+    fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator",
+    fap_version="1.1",
+    fap_description="App allows you to display various barcodes on flipper screen",
+)

+ 369 - 0
barcode_gen/barcode_app.c

@@ -0,0 +1,369 @@
+#include "barcode_app.h"
+
+#include "barcode_app_icons.h"
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
+#include <notification/notification_app.h>
+
+/**
+ * Opens a file browser dialog and returns the filepath of the selected file
+ * 
+ * @param folder  the folder to view when the browser opens
+ * @param file_path a string pointer for the file_path when a file is selected, 
+ *                  file_path will be the folder path is nothing is selected
+ * @returns true if a file is selected
+*/
+
+NotificationApp* notifications = 0;
+
+static bool select_file(const char* folder, FuriString* file_path) {
+    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10);
+    browser_options.base_path = DEFAULT_USER_BARCODES;
+    furi_string_set(file_path, folder);
+
+    bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
+
+    furi_record_close(RECORD_DIALOGS);
+
+    return res;
+}
+
+/**
+ * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data
+*/
+ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) {
+    //Open Storage
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    ErrorCode reason = OKCode;
+
+    if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) {
+        FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path));
+        reason = FileOpening;
+    } else {
+        if(!flipper_format_read_string(ff, "Type", raw_type)) {
+            FURI_LOG_E(TAG, "Could not read \"Type\" string");
+            reason = InvalidFileData;
+        }
+        if(!flipper_format_read_string(ff, "Data", raw_data)) {
+            FURI_LOG_E(TAG, "Could not read \"Data\" string");
+            reason = InvalidFileData;
+        }
+    }
+
+    //Close Storage
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    return reason;
+}
+
+/**
+ * Gets the file name from a file path
+ * @param file_path  the file path
+ * @param file_name  the FuriString to store the file name
+ * @param remove_extension  true if the extension should be removed, otherwise false
+*/
+bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) {
+    if(file_path == NULL || file_name == NULL) {
+        return false;
+    }
+    uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0);
+    if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) {
+        return false;
+    }
+
+    furi_string_set(file_name, file_path);
+    furi_string_right(file_name, slash_index + 1);
+    if(remove_extension) {
+        uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0);
+        if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) {
+            furi_string_left(file_name, ext_index);
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Creates the barcode folder
+*/
+void init_folder() {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FURI_LOG_I(TAG, "Creating barcodes folder");
+    if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) {
+        FURI_LOG_I(TAG, "Barcodes folder successfully created!");
+    } else {
+        FURI_LOG_I(TAG, "Barcodes folder already exists.");
+    }
+    furi_record_close(RECORD_STORAGE);
+}
+
+void select_barcode_item(BarcodeApp* app) {
+    FuriString* file_path = furi_string_alloc();
+    FuriString* raw_type = furi_string_alloc();
+    FuriString* raw_data = furi_string_alloc();
+
+    //this determines if the data was read correctly or if the
+    bool loaded_success = true;
+    ErrorCode reason = OKCode;
+
+    bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
+    if(file_selected) {
+        FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
+        Barcode* barcode = app->barcode_view;
+
+        reason = read_raw_data(file_path, raw_type, raw_data);
+        if(reason != OKCode) {
+            loaded_success = false;
+            FURI_LOG_E(TAG, "Could not read data correctly");
+        }
+
+        //Free the data from the previous barcode
+        barcode_free_model(barcode);
+
+        with_view_model(
+            barcode->view,
+            BarcodeModel * model,
+            {
+                model->file_path = furi_string_alloc_set(file_path);
+
+                model->data = malloc(sizeof(BarcodeData));
+                model->data->valid = loaded_success;
+
+                if(loaded_success) {
+                    model->data->raw_data = furi_string_alloc_set(raw_data);
+                    model->data->correct_data = furi_string_alloc();
+
+                    model->data->type_obj = get_type(raw_type);
+
+                    barcode_loader(model->data);
+                } else {
+                    model->data->reason = reason;
+                }
+            },
+            true);
+
+        view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView);
+    }
+
+    furi_string_free(raw_type);
+    furi_string_free(raw_data);
+    furi_string_free(file_path);
+}
+
+void edit_barcode_item(BarcodeApp* app) {
+    FuriString* file_path = furi_string_alloc();
+    FuriString* file_name = furi_string_alloc();
+    FuriString* raw_type = furi_string_alloc();
+    FuriString* raw_data = furi_string_alloc();
+
+    //this determines if the data was read correctly or if the
+    ErrorCode reason = OKCode;
+
+    bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
+    if(file_selected) {
+        FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
+        CreateView* create_view_object = app->create_view;
+
+        reason = read_raw_data(file_path, raw_type, raw_data);
+        if(reason != OKCode) {
+            FURI_LOG_E(TAG, "Could not read data correctly");
+            with_view_model(
+                app->message_view->view,
+                MessageViewModel * model,
+                { model->message = get_error_code_message(reason); },
+                true);
+
+            view_dispatcher_switch_to_view(
+                create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+
+        } else {
+            BarcodeTypeObj* type_obj = get_type(raw_type);
+            if(type_obj->type == UNKNOWN) {
+                type_obj = barcode_type_objs[0];
+            }
+            get_file_name_from_path(file_path, file_name, true);
+
+            create_view_free_model(create_view_object);
+            with_view_model(
+                create_view_object->view,
+                CreateViewModel * model,
+                {
+                    model->selected_menu_item = 0;
+                    model->barcode_type = type_obj;
+                    model->file_path = furi_string_alloc_set(file_path);
+                    model->file_name = furi_string_alloc_set(file_name);
+                    model->barcode_data = furi_string_alloc_set(raw_data);
+                    model->mode = EditMode;
+                },
+                true);
+            view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
+        }
+    }
+
+    furi_string_free(raw_type);
+    furi_string_free(raw_data);
+    furi_string_free(file_name);
+    furi_string_free(file_path);
+}
+
+void create_barcode_item(BarcodeApp* app) {
+    CreateView* create_view_object = app->create_view;
+
+    create_view_free_model(create_view_object);
+
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            model->selected_menu_item = 0;
+            model->barcode_type = barcode_type_objs[0];
+            model->file_path = furi_string_alloc();
+            model->file_name = furi_string_alloc();
+            model->barcode_data = furi_string_alloc();
+            model->mode = NewMode;
+        },
+        true);
+    view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
+}
+
+void submenu_callback(void* context, uint32_t index) {
+    furi_assert(context);
+
+    BarcodeApp* app = context;
+
+    if(index == SelectBarcodeItem) {
+        select_barcode_item(app);
+    } else if(index == EditBarcodeItem) {
+        edit_barcode_item(app);
+    } else if(index == CreateBarcodeItem) {
+        create_barcode_item(app);
+    }
+}
+
+uint32_t create_view_callback(void* context) {
+    UNUSED(context);
+    return CreateBarcodeView;
+}
+
+uint32_t main_menu_callback(void* context) {
+    UNUSED(context);
+    return MainMenuView;
+}
+
+uint32_t exit_callback(void* context) {
+    UNUSED(context);
+    return VIEW_NONE;
+}
+
+void free_app(BarcodeApp* app) {
+    FURI_LOG_I(TAG, "Freeing Data");
+
+    init_folder();
+    free_types();
+
+    view_dispatcher_remove_view(app->view_dispatcher, TextInputView);
+    text_input_free(app->text_input);
+
+    view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView);
+    message_view_free(app->message_view);
+
+    view_dispatcher_remove_view(app->view_dispatcher, MainMenuView);
+    submenu_free(app->main_menu);
+
+    view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView);
+    create_view_free(app->create_view);
+
+    view_dispatcher_remove_view(app->view_dispatcher, BarcodeView);
+    barcode_free(app->barcode_view);
+
+    //free the dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+
+    furi_message_queue_free(app->event_queue);
+
+    furi_record_close(RECORD_GUI);
+    app->gui = NULL;
+
+    free(app);
+}
+
+void set_backlight_brightness(float brightness) {
+    NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
+    notifications->settings.display_brightness = brightness;
+    notification_message(notifications, &sequence_display_backlight_on);
+}
+
+int32_t barcode_main(void* p) {
+    UNUSED(p);
+    BarcodeApp* app = malloc(sizeof(BarcodeApp));
+    init_types();
+    app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+    // Register view port in GUI
+    app->gui = furi_record_open(RECORD_GUI);
+
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    app->main_menu = submenu_alloc();
+    submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app);
+    view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback);
+    view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu));
+
+    submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app);
+
+    NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
+    // Save original brightness
+    float originalBrightness = notifications->settings.display_brightness;
+    // force backlight and increase brightness
+    notification_message_block(notifications, &sequence_display_backlight_enforce_on);
+    set_backlight_brightness(10); // set to highest
+
+    /*****************************
+     * Creating Text Input View
+     ******************************/
+    app->text_input = text_input_alloc();
+    view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback);
+    view_dispatcher_add_view(
+        app->view_dispatcher, TextInputView, text_input_get_view(app->text_input));
+
+    /*****************************
+     * Creating Message View
+     ******************************/
+    app->message_view = message_view_allocate(app);
+    view_dispatcher_add_view(
+        app->view_dispatcher, MessageErrorView, message_get_view(app->message_view));
+
+    /*****************************
+     * Creating Create View
+     ******************************/
+    app->create_view = create_view_allocate(app);
+    submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app);
+    view_set_previous_callback(create_get_view(app->create_view), main_menu_callback);
+    view_dispatcher_add_view(
+        app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view));
+
+    /*****************************
+     * Creating Barcode View
+     ******************************/
+    app->barcode_view = barcode_view_allocate(app);
+    view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback);
+    view_dispatcher_add_view(
+        app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view));
+
+    //switch view to submenu and run dispatcher
+    view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView);
+    view_dispatcher_run(app->view_dispatcher);
+
+    free_app(app);
+    notification_message_block(notifications, &sequence_display_backlight_enforce_auto);
+    set_backlight_brightness(originalBrightness);
+
+    return 0;
+}

+ 88 - 0
barcode_gen/barcode_app.h

@@ -0,0 +1,88 @@
+#pragma once
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <gui/gui.h>
+#include <input/input.h>
+#include <dialogs/dialogs.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/text_input.h>
+
+#include <flipper_format/flipper_format.h>
+
+#include "barcode_utils.h"
+
+#define TAG "BARCODE"
+#define VERSION "1.1"
+#define FILE_VERSION "1"
+
+#define TEXT_BUFFER_SIZE 128
+
+#define BARCODE_HEIGHT 50
+#define BARCODE_Y_START 3
+
+//the folder where the codabar encoding table is located
+#define CODABAR_DICT_FILE_PATH APP_ASSETS_PATH("codabar_encodings.txt")
+
+//the folder where the code 39 encoding table is located
+#define CODE39_DICT_FILE_PATH APP_ASSETS_PATH("code39_encodings.txt")
+
+//the folder where the code 128 encoding table is located
+#define CODE128_DICT_FILE_PATH APP_ASSETS_PATH("code128_encodings.txt")
+
+//the folder where the code 128 C encoding table is located
+#define CODE128C_DICT_FILE_PATH APP_ASSETS_PATH("code128c_encodings.txt")
+
+//the folder where the user stores their barcodes
+#define DEFAULT_USER_BARCODES EXT_PATH("apps_data/barcodes")
+
+//The extension barcode files use
+#define BARCODE_EXTENSION ".txt"
+#define BARCODE_EXTENSION_LENGTH 4
+
+#include "views/barcode_view.h"
+#include "views/create_view.h"
+#include "views/message_view.h"
+#include "barcode_validator.h"
+
+typedef struct BarcodeApp BarcodeApp;
+
+struct BarcodeApp {
+    Submenu* main_menu;
+    ViewDispatcher* view_dispatcher;
+    Gui* gui;
+
+    FuriMessageQueue* event_queue;
+
+    CreateView* create_view;
+    Barcode* barcode_view;
+
+    MessageView* message_view;
+    TextInput* text_input;
+};
+
+enum SubmenuItems {
+    SelectBarcodeItem,
+    EditBarcodeItem,
+
+    CreateBarcodeItem
+};
+
+enum Views {
+    TextInputView,
+    MessageErrorView,
+    MainMenuView,
+    CreateBarcodeView,
+
+    BarcodeView
+};
+
+void submenu_callback(void* context, uint32_t index);
+
+uint32_t main_menu_callback(void* context);
+
+uint32_t exit_callback(void* context);
+
+int32_t barcode_main(void* p);

+ 22 - 0
barcode_gen/barcode_encoding_files/codabar_encodings.txt

@@ -0,0 +1,22 @@
+# alternates between bars and spaces, always begins with bar
+# 0 for narrow, 1 for wide
+0: 0000011
+1: 0000110
+2: 0001001
+3: 1100000
+4: 0010010
+5: 1000010
+6: 0100001
+7: 0100100
+8: 0110000
+9: 1001000
+-: 0001100
+$: 0011000
+:: 1000101
+/: 1010001
+.: 1010100
++: 0010101
+A: 0011010
+B: 0101001
+C: 0001011
+D: 0001110

+ 202 - 0
barcode_gen/barcode_encoding_files/code128_encodings.txt

@@ -0,0 +1,202 @@
+ : 00
+!: 01
+": 02
+#: 03
+$: 04
+%: 05
+&: 06
+': 07
+(: 08
+): 09
+*: 10
++: 11
+,: 12
+-: 13
+.: 14
+/: 15
+0: 16
+1: 17
+2: 18
+3: 19
+4: 20
+5: 21
+6: 22
+7: 23
+8: 24
+9: 25
+:: 26
+;: 27
+<: 28
+=: 29
+>: 30
+?: 31
+@: 32
+A: 33
+B: 34
+C: 35
+D: 36
+E: 37
+F: 38
+G: 39
+H: 40
+I: 41
+J: 42
+K: 43
+L: 44
+M: 45
+N: 46
+O: 47
+P: 48
+Q: 49
+R: 50
+S: 51
+T: 52
+U: 53
+V: 54
+W: 55
+X: 56
+Y: 57
+Z: 58
+[: 59
+\: 60
+]: 61
+^: 62
+_: 63
+`: 64
+a: 65
+b: 66
+c: 67
+d: 68
+e: 69
+f: 70
+g: 71
+h: 72
+i: 73
+j: 74
+k: 75
+l: 76
+m: 77
+n: 78
+o: 79
+p: 80
+q: 81
+r: 82
+s: 83
+t: 84
+u: 85
+v: 86
+w: 87
+x: 88
+y: 89
+z: 90
+{: 91
+|: 92
+}: 93
+~: 94
+
+00: 11011001100
+01: 11001101100
+02: 11001100110
+03: 10010011000
+04: 10010001100
+05: 10001001100
+06: 10011001000
+07: 10011000100
+08: 10001100100
+09: 11001001000
+10: 11001000100
+11: 11000100100
+12: 10110011100
+13: 10011011100
+14: 10011001110
+15: 10111001100
+16: 10011101100
+17: 10011100110
+18: 11001110010
+19: 11001011100
+20: 11001001110
+21: 11011100100
+22: 11001110100
+23: 11101101110
+24: 11101001100
+25: 11100101100
+26: 11100100110
+27: 11101100100
+28: 11100110100
+29: 11100110010
+30: 11011011000
+31: 11011000110
+32: 11000110110
+33: 10100011000
+34: 10001011000
+35: 10001000110
+36: 10110001000
+37: 10001101000
+38: 10001100010
+39: 11010001000
+40: 11000101000
+41: 11000100010
+42: 10110111000
+43: 10110001110
+44: 10001101110
+45: 10111011000
+46: 10111000110
+47: 10001110110
+48: 11101110110
+49: 11010001110
+50: 11000101110
+51: 11011101000
+52: 11011100010
+53: 11011101110
+54: 11101011000
+55: 11101000110
+56: 11100010110
+57: 11101101000
+58: 11101100010
+59: 11100011010
+60: 11101111010
+61: 11001000010
+62: 11110001010
+63: 10100110000
+64: 10100001100
+65: 10010110000
+66: 10010000110
+67: 10000101100
+68: 10000100110
+69: 10110010000
+70: 10110000100
+71: 10011010000
+72: 10011000010
+73: 10000110100
+74: 10000110010
+75: 11000010010
+76: 11001010000
+77: 11110111010
+78: 11000010100
+79: 10001111010
+80: 10100111100
+81: 10010111100
+82: 10010011110
+83: 10111100100
+84: 10011110100
+85: 10011110010
+86: 11110100100
+87: 11110010100
+88: 11110010010
+89: 11011011110
+90: 11011110110
+91: 11110110110
+92: 10101111000
+93: 10100011110
+94: 10001011110
+95: 10111101000
+96: 10111100010
+97: 11110101000
+98: 11110100010
+99: 10111011110
+100: 10111101110
+101: 11101011110
+102: 11110101110
+103: 11010000100
+104: 11010010000
+105: 11010011100

+ 106 - 0
barcode_gen/barcode_encoding_files/code128c_encodings.txt

@@ -0,0 +1,106 @@
+00: 11011001100
+01: 11001101100
+02: 11001100110
+03: 10010011000
+04: 10010001100
+05: 10001001100
+06: 10011001000
+07: 10011000100
+08: 10001100100
+09: 11001001000
+10: 11001000100
+11: 11000100100
+12: 10110011100
+13: 10011011100
+14: 10011001110
+15: 10111001100
+16: 10011101100
+17: 10011100110
+18: 11001110010
+19: 11001011100
+20: 11001001110
+21: 11011100100
+22: 11001110100
+23: 11101101110
+24: 11101001100
+25: 11100101100
+26: 11100100110
+27: 11101100100
+28: 11100110100
+29: 11100110010
+30: 11011011000
+31: 11011000110
+32: 11000110110
+33: 10100011000
+34: 10001011000
+35: 10001000110
+36: 10110001000
+37: 10001101000
+38: 10001100010
+39: 11010001000
+40: 11000101000
+41: 11000100010
+42: 10110111000
+43: 10110001110
+44: 10001101110
+45: 10111011000
+46: 10111000110
+47: 10001110110
+48: 11101110110
+49: 11010001110
+50: 11000101110
+51: 11011101000
+52: 11011100010
+53: 11011101110
+54: 11101011000
+55: 11101000110
+56: 11100010110
+57: 11101101000
+58: 11101100010
+59: 11100011010
+60: 11101111010
+61: 11001000010
+62: 11110001010
+63: 10100110000
+64: 10100001100
+65: 10010110000
+66: 10010000110
+67: 10000101100
+68: 10000100110
+69: 10110010000
+70: 10110000100
+71: 10011010000
+72: 10011000010
+73: 10000110100
+74: 10000110010
+75: 11000010010
+76: 11001010000
+77: 11110111010
+78: 11000010100
+79: 10001111010
+80: 10100111100
+81: 10010111100
+82: 10010011110
+83: 10111100100
+84: 10011110100
+85: 10011110010
+86: 11110100100
+87: 11110010100
+88: 11110010010
+89: 11011011110
+90: 11011110110
+91: 11110110110
+92: 10101111000
+93: 10100011110
+94: 10001011110
+95: 10111101000
+96: 10111100010
+97: 11110101000
+98: 11110100010
+99: 10111011110
+100: 10111101110
+101: 11101011110
+102: 11110101110
+103: 11010000100
+104: 11010010000
+105: 11010011100

+ 44 - 0
barcode_gen/barcode_encoding_files/code39_encodings.txt

@@ -0,0 +1,44 @@
+0: 000110100
+1: 100100001
+2: 001100001
+3: 101100000
+4: 000110001
+5: 100110000
+6: 001110000
+7: 000100101
+8: 100100100
+9: 001100100
+A: 100001001
+B: 001001001
+C: 101001000
+D: 000011001
+E: 100011000
+F: 001011000
+G: 000001101
+H: 100001100
+I: 001001100
+J: 000011100
+K: 100000011
+L: 001000011
+M: 101000010
+N: 000010011
+O: 100010010
+P: 001010010
+Q: 000000111
+R: 100000110
+S: 001000110
+T: 000010110
+U: 110000001
+V: 011000001
+W: 111000000
+X: 010010001
+Y: 110010000
+Z: 011010000
+-: 010000101
+.: 110000100
+ : 011000100
+*: 010010100
+$: 010101000
+/: 010100010
++: 010001010
+%: 000101010

+ 147 - 0
barcode_gen/barcode_utils.c

@@ -0,0 +1,147 @@
+#include "barcode_utils.h"
+
+BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES] = {NULL};
+
+void init_types() {
+    BarcodeTypeObj* upc_a = malloc(sizeof(BarcodeTypeObj));
+    upc_a->name = "UPC-A";
+    upc_a->type = UPCA;
+    upc_a->min_digits = 11;
+    upc_a->max_digits = 12;
+    upc_a->start_pos = 16;
+    barcode_type_objs[UPCA] = upc_a;
+
+    BarcodeTypeObj* ean_8 = malloc(sizeof(BarcodeTypeObj));
+    ean_8->name = "EAN-8";
+    ean_8->type = EAN8;
+    ean_8->min_digits = 7;
+    ean_8->max_digits = 8;
+    ean_8->start_pos = 32;
+    barcode_type_objs[EAN8] = ean_8;
+
+    BarcodeTypeObj* ean_13 = malloc(sizeof(BarcodeTypeObj));
+    ean_13->name = "EAN-13";
+    ean_13->type = EAN13;
+    ean_13->min_digits = 12;
+    ean_13->max_digits = 13;
+    ean_13->start_pos = 16;
+    barcode_type_objs[EAN13] = ean_13;
+
+    BarcodeTypeObj* code_39 = malloc(sizeof(BarcodeTypeObj));
+    code_39->name = "CODE-39";
+    code_39->type = CODE39;
+    code_39->min_digits = 1;
+    code_39->max_digits = -1;
+    code_39->start_pos = 0;
+    barcode_type_objs[CODE39] = code_39;
+
+    BarcodeTypeObj* code_128 = malloc(sizeof(BarcodeTypeObj));
+    code_128->name = "CODE-128";
+    code_128->type = CODE128;
+    code_128->min_digits = 1;
+    code_128->max_digits = -1;
+    code_128->start_pos = 0;
+    barcode_type_objs[CODE128] = code_128;
+
+    BarcodeTypeObj* code_128c = malloc(sizeof(BarcodeTypeObj));
+    code_128c->name = "CODE-128C";
+    code_128c->type = CODE128C;
+    code_128c->min_digits = 2;
+    code_128c->max_digits = -1;
+    code_128c->start_pos = 0;
+    barcode_type_objs[CODE128C] = code_128c;
+
+    BarcodeTypeObj* codabar = malloc(sizeof(BarcodeTypeObj));
+    codabar->name = "Codabar";
+    codabar->type = CODABAR;
+    codabar->min_digits = 1;
+    codabar->max_digits = -1;
+    codabar->start_pos = 0;
+    barcode_type_objs[CODABAR] = codabar;
+
+    BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj));
+    unknown->name = "Unknown";
+    unknown->type = UNKNOWN;
+    unknown->min_digits = 0;
+    unknown->max_digits = 0;
+    unknown->start_pos = 0;
+    barcode_type_objs[UNKNOWN] = unknown;
+}
+
+void free_types() {
+    for(int i = 0; i < NUMBER_OF_BARCODE_TYPES; i++) {
+        free(barcode_type_objs[i]);
+    }
+}
+
+BarcodeTypeObj* get_type(FuriString* type_string) {
+    if(furi_string_cmp_str(type_string, "UPC-A") == 0) {
+        return barcode_type_objs[UPCA];
+    }
+    if(furi_string_cmp_str(type_string, "EAN-8") == 0) {
+        return barcode_type_objs[EAN8];
+    }
+    if(furi_string_cmp_str(type_string, "EAN-13") == 0) {
+        return barcode_type_objs[EAN13];
+    }
+    if(furi_string_cmp_str(type_string, "CODE-39") == 0) {
+        return barcode_type_objs[CODE39];
+    }
+    if(furi_string_cmp_str(type_string, "CODE-128") == 0) {
+        return barcode_type_objs[CODE128];
+    }
+    if(furi_string_cmp_str(type_string, "CODE-128C") == 0) {
+        return barcode_type_objs[CODE128C];
+    }
+    if(furi_string_cmp_str(type_string, "Codabar") == 0) {
+        return barcode_type_objs[CODABAR];
+    }
+
+    return barcode_type_objs[UNKNOWN];
+}
+
+const char* get_error_code_name(ErrorCode error_code) {
+    switch(error_code) {
+    case WrongNumberOfDigits:
+        return "Wrong Number Of Digits";
+    case InvalidCharacters:
+        return "Invalid Characters";
+    case UnsupportedType:
+        return "Unsupported Type";
+    case FileOpening:
+        return "File Opening Error";
+    case InvalidFileData:
+        return "Invalid File Data";
+    case MissingEncodingTable:
+        return "Missing Encoding Table";
+    case EncodingTableError:
+        return "Encoding Table Error";
+    case OKCode:
+        return "OK";
+    default:
+        return "Unknown Code";
+    };
+}
+
+const char* get_error_code_message(ErrorCode error_code) {
+    switch(error_code) {
+    case WrongNumberOfDigits:
+        return "Wrong # of characters";
+    case InvalidCharacters:
+        return "Invalid characters";
+    case UnsupportedType:
+        return "Unsupported barcode type";
+    case FileOpening:
+        return "Could not open file";
+    case InvalidFileData:
+        return "Invalid file data";
+    case MissingEncodingTable:
+        return "Missing encoding table";
+    case EncodingTableError:
+        return "Encoding table error";
+    case OKCode:
+        return "OK";
+    default:
+        return "Could not read barcode data";
+    };
+}

+ 55 - 0
barcode_gen/barcode_utils.h

@@ -0,0 +1,55 @@
+
+#pragma once
+#include <furi.h>
+#include <furi_hal.h>
+
+#define NUMBER_OF_BARCODE_TYPES 8
+
+typedef enum {
+    WrongNumberOfDigits, //There is too many or too few digits in the barcode
+    InvalidCharacters, //The barcode contains invalid characters
+    UnsupportedType, //the barcode type is not supported
+    FileOpening, //A problem occurred when opening the barcode data file
+    InvalidFileData, //One of the key in the file doesn't exist or there is a typo
+    MissingEncodingTable, //The encoding table txt for the barcode type is missing
+    EncodingTableError, //Something is wrong with the encoding table, probably missing data or typo
+    OKCode
+} ErrorCode;
+
+typedef enum {
+    UPCA,
+    EAN8,
+    EAN13,
+    CODE39,
+    CODE128,
+    CODE128C,
+    CODABAR,
+
+    UNKNOWN
+} BarcodeType;
+
+typedef struct {
+    char* name; //The name of the barcode type
+    BarcodeType type; //The barcode type enum
+    int min_digits; //the minimum number of digits
+    int max_digits; //the maximum number of digits
+    int start_pos; //where to start drawing the barcode, set to -1 to dynamically draw barcode
+} BarcodeTypeObj;
+
+typedef struct {
+    BarcodeTypeObj* type_obj;
+    int check_digit; //A place to store the check digit
+    FuriString* raw_data; //the data directly from the file
+    FuriString* correct_data; //the corrected/processed data
+    bool valid; //true if the raw data is correctly formatted, such as correct num of digits, valid characters, etc.
+    ErrorCode reason; //the reason why this barcode is invalid
+} BarcodeData;
+
+//All available barcode types
+extern BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES];
+
+void init_types();
+void free_types();
+BarcodeTypeObj* get_type(FuriString* type_string);
+const char* get_error_code_name(ErrorCode error_code);
+const char* get_error_code_message(ErrorCode error_code);

+ 532 - 0
barcode_gen/barcode_validator.c

@@ -0,0 +1,532 @@
+#include "barcode_validator.h"
+
+void barcode_loader(BarcodeData* barcode_data) {
+    switch(barcode_data->type_obj->type) {
+    case UPCA:
+    case EAN8:
+    case EAN13:
+        ean_upc_loader(barcode_data);
+        break;
+    case CODE39:
+        code_39_loader(barcode_data);
+        break;
+    case CODE128:
+        code_128_loader(barcode_data);
+        break;
+    case CODE128C:
+        code_128c_loader(barcode_data);
+        break;
+    case CODABAR:
+        codabar_loader(barcode_data);
+        break;
+    case UNKNOWN:
+        barcode_data->reason = UnsupportedType;
+        barcode_data->valid = false;
+    default:
+        break;
+    }
+}
+
+/**
+ * Calculates the check digit of a barcode if they have one
+ * @param barcode_data the barcode data
+ * @returns a check digit or -1 for either an invalid 
+*/
+int calculate_check_digit(BarcodeData* barcode_data) {
+    int check_digit = -1;
+    switch(barcode_data->type_obj->type) {
+    case UPCA:
+    case EAN8:
+    case EAN13:
+        check_digit = calculate_ean_upc_check_digit(barcode_data);
+        break;
+    case CODE39:
+    case CODE128:
+    case CODE128C:
+    case CODABAR:
+    case UNKNOWN:
+    default:
+        break;
+    }
+
+    return check_digit;
+}
+
+/**
+ * Calculates the check digit of barcode types UPC-A, EAN-8, & EAN-13
+*/
+int calculate_ean_upc_check_digit(BarcodeData* barcode_data) {
+    int check_digit = 0;
+    int odd = 0;
+    int even = 0;
+
+    int length = barcode_data->type_obj->min_digits;
+
+    //Get sum of odd digits
+    for(int i = 0; i < length; i += 2) {
+        odd += furi_string_get_char(barcode_data->raw_data, i) - '0';
+    }
+
+    //Get sum of even digits
+    for(int i = 1; i < length; i += 2) {
+        even += furi_string_get_char(barcode_data->raw_data, i) - '0';
+    }
+
+    if(barcode_data->type_obj->type == EAN13) {
+        check_digit = even * 3 + odd;
+    } else {
+        check_digit = odd * 3 + even;
+    }
+
+    check_digit = check_digit % 10;
+
+    return (10 - check_digit) % 10;
+}
+
+/**
+ * Loads and validates Barcode Types EAN-8, EAN-13, and UPC-A
+ * barcode_data and its strings should already be allocated;
+*/
+void ean_upc_loader(BarcodeData* barcode_data) {
+    int barcode_length = furi_string_size(barcode_data->raw_data);
+
+    int min_digits = barcode_data->type_obj->min_digits;
+    int max_digit = barcode_data->type_obj->max_digits;
+
+    //check the length of the barcode
+    if(barcode_length < min_digits || barcode_length > max_digit) {
+        barcode_data->reason = WrongNumberOfDigits;
+        barcode_data->valid = false;
+        return;
+    }
+
+    //checks if the barcode contains any characters that aren't a number
+    for(int i = 0; i < barcode_length; i++) {
+        char character = furi_string_get_char(barcode_data->raw_data, i);
+        int digit = character - '0'; //convert the number into an int (also the index)
+        if(digit < 0 || digit > 9) {
+            barcode_data->reason = InvalidCharacters;
+            barcode_data->valid = false;
+            return;
+        }
+    }
+
+    int check_digit = calculate_check_digit(barcode_data);
+    char check_digit_char = check_digit + '0';
+
+    barcode_data->check_digit = check_digit;
+
+    //if the barcode length is at max length then we will verify if the check digit is correct
+    if(barcode_length == max_digit) {
+        //append the raw_data to the correct data string
+        furi_string_cat(barcode_data->correct_data, barcode_data->raw_data);
+
+        //append the check digit to the correct data string
+        furi_string_set_char(barcode_data->correct_data, min_digits, check_digit_char);
+    }
+    //if the barcode length is at min length, we will calculate the check digit
+    if(barcode_length == min_digits) {
+        //append the raw_data to the correct data string
+        furi_string_cat(barcode_data->correct_data, barcode_data->raw_data);
+
+        //append the check digit to the correct data string
+        furi_string_push_back(barcode_data->correct_data, check_digit_char);
+    }
+}
+
+void code_39_loader(BarcodeData* barcode_data) {
+    int barcode_length = furi_string_size(barcode_data->raw_data);
+
+    int min_digits = barcode_data->type_obj->min_digits;
+
+    //check the length of the barcode, must contain atleast a character,
+    //this can have as many characters as it wants, it might not fit on the screen
+    if(barcode_length < min_digits) {
+        barcode_data->reason = WrongNumberOfDigits;
+        barcode_data->valid = false;
+        return;
+    }
+
+    FuriString* barcode_bits = furi_string_alloc();
+    FuriString* temp_string = furi_string_alloc();
+
+    //add starting and ending *
+    if(!furi_string_start_with(barcode_data->raw_data, "*")) {
+        furi_string_push_back(temp_string, '*');
+        furi_string_cat(temp_string, barcode_data->raw_data);
+        furi_string_set(barcode_data->raw_data, temp_string);
+    }
+
+    if(!furi_string_end_with(barcode_data->raw_data, "*")) {
+        furi_string_push_back(barcode_data->raw_data, '*');
+    }
+
+    furi_string_free(temp_string);
+    barcode_length = furi_string_size(barcode_data->raw_data);
+
+    //Open Storage
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    if(!flipper_format_file_open_existing(ff, CODE39_DICT_FILE_PATH)) {
+        FURI_LOG_E(TAG, "Could not open file %s", CODE39_DICT_FILE_PATH);
+        barcode_data->reason = MissingEncodingTable;
+        barcode_data->valid = false;
+    } else {
+        FuriString* char_bits = furi_string_alloc();
+        for(int i = 0; i < barcode_length; i++) {
+            char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i));
+
+            //convert a char into a string so it used in flipper_format_read_string
+            char current_character[2];
+            snprintf(current_character, 2, "%c", barcode_char);
+
+            if(!flipper_format_read_string(ff, current_character, char_bits)) {
+                FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+                barcode_data->reason = InvalidCharacters;
+                barcode_data->valid = false;
+                break;
+            } else {
+                FURI_LOG_I(
+                    TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits));
+                furi_string_cat(barcode_bits, char_bits);
+            }
+            flipper_format_rewind(ff);
+        }
+        furi_string_free(char_bits);
+    }
+
+    //Close Storage
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    furi_string_cat(barcode_data->correct_data, barcode_bits);
+    furi_string_free(barcode_bits);
+}
+
+/**
+ * Loads a code 128 barcode
+ * 
+ * Only supports character set B
+*/
+void code_128_loader(BarcodeData* barcode_data) {
+    int barcode_length = furi_string_size(barcode_data->raw_data);
+
+    //the start code for character set B
+    int start_code_value = 104;
+
+    //The bits for the start code
+    const char* start_code_bits = "11010010000";
+
+    //The bits for the stop code
+    const char* stop_code_bits = "1100011101011";
+
+    int min_digits = barcode_data->type_obj->min_digits;
+
+    /**
+     * A sum of all of the characters values
+     * Ex: 
+     * Barcode Data : ABC
+     * A has a value of 33
+     * B has a value of 34
+     * C has a value of 35
+     * 
+     * the checksum_adder would be (33 * 1) + (34 * 2) + (35 * 3) + 104 = 310
+     * 
+     * Add 104 since we are using set B
+     */
+    int checksum_adder = start_code_value;
+    /**
+     * Checksum digits is the number of characters it has read so far
+     * In the above example the checksum_digits would be 3
+    */
+    int checksum_digits = 0;
+
+    //the calculated check digit
+    int final_check_digit = 0;
+
+    //check the length of the barcode, must contain atleast a character,
+    //this can have as many characters as it wants, it might not fit on the screen
+    if(barcode_length < min_digits) {
+        barcode_data->reason = WrongNumberOfDigits;
+        barcode_data->valid = false;
+        return;
+    }
+
+    //Open Storage
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    FuriString* barcode_bits = furi_string_alloc();
+
+    //add the start code
+    furi_string_cat(barcode_bits, start_code_bits);
+
+    if(!flipper_format_file_open_existing(ff, CODE128_DICT_FILE_PATH)) {
+        FURI_LOG_E(TAG, "Could not open file %s", CODE128_DICT_FILE_PATH);
+        barcode_data->reason = MissingEncodingTable;
+        barcode_data->valid = false;
+    } else {
+        FuriString* value = furi_string_alloc();
+        FuriString* char_bits = furi_string_alloc();
+        for(int i = 0; i < barcode_length; i++) {
+            char barcode_char = furi_string_get_char(barcode_data->raw_data, i);
+
+            //convert a char into a string so it used in flipper_format_read_string
+            char current_character[2];
+            snprintf(current_character, 2, "%c", barcode_char);
+
+            //get the value of the character
+            if(!flipper_format_read_string(ff, current_character, value)) {
+                FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+                barcode_data->reason = InvalidCharacters;
+                barcode_data->valid = false;
+                break;
+            }
+            //using the value of the character, get the characters bits
+            if(!flipper_format_read_string(ff, furi_string_get_cstr(value), char_bits)) {
+                FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+                barcode_data->reason = EncodingTableError;
+                barcode_data->valid = false;
+                break;
+            } else {
+                //add the bits to the full barcode
+                furi_string_cat(barcode_bits, char_bits);
+
+                //calculate the checksum
+                checksum_digits += 1;
+                checksum_adder += (atoi(furi_string_get_cstr(value)) * checksum_digits);
+
+                FURI_LOG_D(
+                    TAG,
+                    "\"%c\" string: %s : %s : %d : %d : %d",
+                    barcode_char,
+                    furi_string_get_cstr(char_bits),
+                    furi_string_get_cstr(value),
+                    checksum_digits,
+                    (atoi(furi_string_get_cstr(value)) * checksum_digits),
+                    checksum_adder);
+            }
+            //bring the file pointer back to the beginning
+            flipper_format_rewind(ff);
+        }
+
+        //calculate the check digit and convert it into a c string for lookup in the encoding table
+        final_check_digit = checksum_adder % 103;
+        int length = snprintf(NULL, 0, "%d", final_check_digit);
+        char* final_check_digit_string = malloc(length + 1);
+        snprintf(final_check_digit_string, length + 1, "%d", final_check_digit);
+
+        //after the checksum has been calculated, add the bits to the full barcode
+        if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) {
+            FURI_LOG_E(TAG, "Could not read \"%s\" string", final_check_digit_string);
+            barcode_data->reason = EncodingTableError;
+            barcode_data->valid = false;
+        } else {
+            //add the check digit bits to the full barcode
+            furi_string_cat(barcode_bits, char_bits);
+
+            FURI_LOG_D(
+                TAG,
+                "\"%s\" string: %s",
+                final_check_digit_string,
+                furi_string_get_cstr(char_bits));
+        }
+
+        free(final_check_digit_string);
+        furi_string_free(value);
+        furi_string_free(char_bits);
+    }
+
+    //add the stop code
+    furi_string_cat(barcode_bits, stop_code_bits);
+
+    //Close Storage
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    furi_string_cat(barcode_data->correct_data, barcode_bits);
+    furi_string_free(barcode_bits);
+}
+
+/**
+ * Loads a code 128 C barcode
+*/
+void code_128c_loader(BarcodeData* barcode_data) {
+    int barcode_length = furi_string_size(barcode_data->raw_data);
+
+    //the start code for character set C
+    int start_code_value = 105;
+
+    //The bits for the start code
+    const char* start_code_bits = "11010011100";
+
+    //The bits for the stop code
+    const char* stop_code_bits = "1100011101011";
+
+    int min_digits = barcode_data->type_obj->min_digits;
+
+    int checksum_adder = start_code_value;
+    int checksum_digits = 0;
+
+    //the calculated check digit
+    int final_check_digit = 0;
+
+    // check the length of the barcode, must contain atleast 2 character,
+    // this can have as many characters as it wants, it might not fit on the screen
+    // code 128 C: the length must be even
+    if((barcode_length < min_digits) || (barcode_length & 1)) {
+        barcode_data->reason = WrongNumberOfDigits;
+        barcode_data->valid = false;
+        return;
+    }
+    //Open Storage
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    FuriString* barcode_bits = furi_string_alloc();
+
+    //add the start code
+    furi_string_cat(barcode_bits, start_code_bits);
+
+    if(!flipper_format_file_open_existing(ff, CODE128C_DICT_FILE_PATH)) {
+        FURI_LOG_E(TAG, "c128c Could not open file %s", CODE128C_DICT_FILE_PATH);
+        barcode_data->reason = MissingEncodingTable;
+        barcode_data->valid = false;
+    } else {
+        FuriString* value = furi_string_alloc();
+        FuriString* char_bits = furi_string_alloc();
+        for(int i = 0; i < barcode_length; i += 2) {
+            char barcode_char1 = furi_string_get_char(barcode_data->raw_data, i);
+            char barcode_char2 = furi_string_get_char(barcode_data->raw_data, i + 1);
+            FURI_LOG_I(TAG, "c128c bc1='%c' bc2='%c'", barcode_char1, barcode_char2);
+
+            char current_chars[4];
+            snprintf(current_chars, 3, "%c%c", barcode_char1, barcode_char2);
+            FURI_LOG_I(TAG, "c128c current_chars='%s'", current_chars);
+
+            //using the value of the characters, get the characters bits
+            if(!flipper_format_read_string(ff, current_chars, char_bits)) {
+                FURI_LOG_E(TAG, "c128c Could not read \"%s\" string", current_chars);
+                barcode_data->reason = EncodingTableError;
+                barcode_data->valid = false;
+                break;
+            } else {
+                //add the bits to the full barcode
+                furi_string_cat(barcode_bits, char_bits);
+
+                // calculate the checksum
+                checksum_digits += 1;
+                checksum_adder += (atoi(current_chars) * checksum_digits);
+
+                FURI_LOG_I(
+                    TAG,
+                    "c128c \"%s\" string: %s : %s : %d : %d : %d",
+                    current_chars,
+                    furi_string_get_cstr(char_bits),
+                    furi_string_get_cstr(value),
+                    checksum_digits,
+                    (atoi(furi_string_get_cstr(value)) * checksum_digits),
+                    checksum_adder);
+            }
+            //bring the file pointer back to the begining
+            flipper_format_rewind(ff);
+        }
+        //calculate the check digit and convert it into a c string for lookup in the encoding table
+        final_check_digit = checksum_adder % 103;
+        FURI_LOG_I(TAG, "c128c finale_check_digit=%d", final_check_digit);
+
+        int length = snprintf(NULL, 0, "%d", final_check_digit);
+        if(final_check_digit < 100) length = 2;
+        char* final_check_digit_string = malloc(length + 1);
+        snprintf(final_check_digit_string, length + 1, "%02d", final_check_digit);
+
+        //after the checksum has been calculated, add the bits to the full barcode
+        if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) {
+            FURI_LOG_E(TAG, "c128c cksum Could not read \"%s\" string", final_check_digit_string);
+            barcode_data->reason = EncodingTableError;
+            barcode_data->valid = false;
+        } else {
+            //add the check digit bits to the full barcode
+            furi_string_cat(barcode_bits, char_bits);
+
+            FURI_LOG_I(
+                TAG,
+                "check digit \"%s\" string: %s",
+                final_check_digit_string,
+                furi_string_get_cstr(char_bits));
+        }
+
+        free(final_check_digit_string);
+        furi_string_free(value);
+        furi_string_free(char_bits);
+    }
+
+    //add the stop code
+    furi_string_cat(barcode_bits, stop_code_bits);
+
+    //Close Storage
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    FURI_LOG_I(TAG, "c128c %s", furi_string_get_cstr(barcode_bits));
+    furi_string_cat(barcode_data->correct_data, barcode_bits);
+    furi_string_free(barcode_bits);
+}
+
+void codabar_loader(BarcodeData* barcode_data) {
+    int barcode_length = furi_string_size(barcode_data->raw_data);
+
+    int min_digits = barcode_data->type_obj->min_digits;
+
+    //check the length of the barcode, must contain atleast a character,
+    //this can have as many characters as it wants, it might not fit on the screen
+    if(barcode_length < min_digits) {
+        barcode_data->reason = WrongNumberOfDigits;
+        barcode_data->valid = false;
+        return;
+    }
+
+    FuriString* barcode_bits = furi_string_alloc();
+
+    barcode_length = furi_string_size(barcode_data->raw_data);
+
+    //Open Storage
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    if(!flipper_format_file_open_existing(ff, CODABAR_DICT_FILE_PATH)) {
+        FURI_LOG_E(TAG, "Could not open file %s", CODABAR_DICT_FILE_PATH);
+        barcode_data->reason = MissingEncodingTable;
+        barcode_data->valid = false;
+    } else {
+        FuriString* char_bits = furi_string_alloc();
+        for(int i = 0; i < barcode_length; i++) {
+            char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i));
+
+            //convert a char into a string so it used in flipper_format_read_string
+            char current_character[2];
+            snprintf(current_character, 2, "%c", barcode_char);
+
+            if(!flipper_format_read_string(ff, current_character, char_bits)) {
+                FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+                barcode_data->reason = InvalidCharacters;
+                barcode_data->valid = false;
+                break;
+            } else {
+                FURI_LOG_I(
+                    TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits));
+                furi_string_cat(barcode_bits, char_bits);
+            }
+            flipper_format_rewind(ff);
+        }
+        furi_string_free(char_bits);
+    }
+
+    //Close Storage
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    furi_string_cat(barcode_data->correct_data, barcode_bits);
+    furi_string_free(barcode_bits);
+}

+ 15 - 0
barcode_gen/barcode_validator.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include "barcode_app.h"
+
+int calculate_check_digit(BarcodeData* barcode_data);
+int calculate_ean_upc_check_digit(BarcodeData* barcode_data);
+void ean_upc_loader(BarcodeData* barcode_data);
+void upc_a_loader(BarcodeData* barcode_data);
+void ean_8_loader(BarcodeData* barcode_data);
+void ean_13_loader(BarcodeData* barcode_data);
+void code_39_loader(BarcodeData* barcode_data);
+void code_128_loader(BarcodeData* barcode_data);
+void code_128c_loader(BarcodeData* barcode_data);
+void codabar_loader(BarcodeData* barcode_data);
+void barcode_loader(BarcodeData* barcode_data);

+ 52 - 0
barcode_gen/encodings.c

@@ -0,0 +1,52 @@
+#include "encodings.h"
+
+const char EAN_13_STRUCTURE_CODES[10][6] = {
+    "LLLLLL",
+    "LLGLGG",
+    "LLGGLG",
+    "LLGGGL",
+    "LGLLGG",
+    "LGGLLG",
+    "LGGGLL",
+    "LGLGLG",
+    "LGLGGL",
+    "LGGLGL"};
+
+const char UPC_EAN_L_CODES[10][8] = {
+    "0001101", // 0
+    "0011001", // 1
+    "0010011", // 2
+    "0111101", // 3
+    "0100011", // 4
+    "0110001", // 5
+    "0101111", // 6
+    "0111011", // 7
+    "0110111", // 8
+    "0001011" // 9
+};
+
+const char EAN_G_CODES[10][8] = {
+    "0100111", // 0
+    "0110011", // 1
+    "0011011", // 2
+    "0100001", // 3
+    "0011101", // 4
+    "0111001", // 5
+    "0000101", // 6
+    "0010001", // 7
+    "0001001", // 8
+    "0010111" // 9
+};
+
+const char UPC_EAN_R_CODES[10][8] = {
+    "1110010", // 0
+    "1100110", // 1
+    "1101100", // 2
+    "1000010", // 3
+    "1011100", // 4
+    "1001110", // 5
+    "1010000", // 6
+    "1000100", // 7
+    "1001000", // 8
+    "1110100" // 9
+};

+ 6 - 0
barcode_gen/encodings.h

@@ -0,0 +1,6 @@
+#pragma once
+
+extern const char EAN_13_STRUCTURE_CODES[10][6];
+extern const char UPC_EAN_L_CODES[10][8];
+extern const char EAN_G_CODES[10][8];
+extern const char UPC_EAN_R_CODES[10][8];

BIN
barcode_gen/images/barcode_10.png


BIN
barcode_gen/img/1.png


BIN
barcode_gen/img/2.png


BIN
barcode_gen/img/3.png


BIN
barcode_gen/img/Codabar Data Example.png


BIN
barcode_gen/img/Creating Barcode.png


BIN
barcode_gen/img/Flipper Barcode.png


BIN
barcode_gen/img/Flipper Box Barcode.png


BIN
barcode_gen/screenshots/Codabar Data Example.png


BIN
barcode_gen/screenshots/Creating Barcode.png


BIN
barcode_gen/screenshots/Flipper Barcode.png


BIN
barcode_gen/screenshots/Flipper Box Barcode.png


+ 510 - 0
barcode_gen/views/barcode_view.c

@@ -0,0 +1,510 @@
+#include "../barcode_app.h"
+#include "barcode_view.h"
+#include "../encodings.h"
+
+/**
+ * @brief Draws a single bit from a barcode at a specified location
+ * @param canvas 
+ * @param bit  a 1 or a 0 to signify a bit of data
+ * @param x  the top left x coordinate
+ * @param y  the top left y coordinate
+ * @param width  the width of the bit
+ * @param height  the height of the bit
+ */
+static void draw_bit(Canvas* canvas, int bit, int x, int y, int width, int height) {
+    if(bit == 1) {
+        canvas_set_color(canvas, ColorBlack);
+    } else {
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_box(canvas, x, y, width, height);
+}
+
+/**
+ * 
+*/
+static void draw_error_str(Canvas* canvas, const char* error) {
+    canvas_clear(canvas);
+    canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+}
+
+/**
+ * @param bits  a string of 1's and 0's
+ * @returns the x coordinate after the bits have been drawn, useful for drawing the next section of bits
+*/
+static int draw_bits(Canvas* canvas, const char* bits, int x, int y, int width, int height) {
+    int bits_length = strlen(bits);
+    for(int i = 0; i < bits_length; i++) {
+        char c = bits[i];
+        int num = c - '0';
+
+        draw_bit(canvas, num, x, y, width, height);
+
+        x += width;
+    }
+    return x;
+}
+
+/**
+ * Draws an EAN-8 type barcode, does not check if the barcode is valid
+ * @param canvas  the canvas
+ * @param barcode_digits  the digits in the barcode, must be 8 characters long
+*/
+static void draw_ean_8(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* barcode_digits = barcode_data->correct_data;
+    BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+    int barcode_length = furi_string_size(barcode_digits);
+
+    int x = type_obj->start_pos;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+
+    //the guard patterns for the beginning, center, ending
+    const char* end_bits = "101";
+    const char* center_bits = "01010";
+
+    //draw the starting guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+    FuriString* code_part = furi_string_alloc();
+
+    //loop through each digit, find the encoding, and draw it
+    for(int i = 0; i < barcode_length; i++) {
+        char current_digit = furi_string_get_char(barcode_digits, i);
+
+        //the actual number and the index of the bits
+        int index = current_digit - '0';
+        //use the L-codes for the first 4 digits and the R-Codes for the last 4 digits
+        if(i <= 3) {
+            furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+        } else {
+            furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+        }
+
+        //convert the current_digit char into a string so it can be printed
+        char current_digit_string[2];
+        snprintf(current_digit_string, 2, "%c", current_digit);
+
+        //set the canvas color to black to print the digit
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+        //draw the bits of the barcode
+        x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+        //if the index has reached 3, that means 4 digits have been drawn and now draw the center guard pattern
+        if(i == 3) {
+            x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+        }
+    }
+    furi_string_free(code_part);
+
+    //draw the ending guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+static void draw_ean_13(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* barcode_digits = barcode_data->correct_data;
+    BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+    int barcode_length = furi_string_size(barcode_digits);
+
+    int x = type_obj->start_pos;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+
+    //the guard patterns for the beginning, center, ending
+    const char* end_bits = "101";
+    const char* center_bits = "01010";
+
+    //draw the starting guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+    FuriString* left_structure = furi_string_alloc();
+    FuriString* code_part = furi_string_alloc();
+
+    //loop through each digit, find the encoding, and draw it
+    for(int i = 0; i < barcode_length; i++) {
+        char current_digit = furi_string_get_char(barcode_digits, i);
+        int index = current_digit - '0';
+
+        if(i == 0) {
+            furi_string_set_str(left_structure, EAN_13_STRUCTURE_CODES[index]);
+
+            //convert the current_digit char into a string so it can be printed
+            char current_digit_string[2];
+            snprintf(current_digit_string, 2, "%c", current_digit);
+
+            //set the canvas color to black to print the digit
+            canvas_set_color(canvas, ColorBlack);
+            canvas_draw_str(canvas, x - 10, y + height + 8, current_digit_string);
+
+            continue;
+        } else {
+            //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits
+            if(i <= 6) {
+                //get the encoding type at the current barcode bit position
+                char encoding_type = furi_string_get_char(left_structure, i - 1);
+                if(encoding_type == 'L') {
+                    furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+                } else {
+                    furi_string_set_str(code_part, EAN_G_CODES[index]);
+                }
+            } else {
+                furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+            }
+
+            //convert the current_digit char into a string so it can be printed
+            char current_digit_string[2];
+            snprintf(current_digit_string, 2, "%c", current_digit);
+
+            //set the canvas color to black to print the digit
+            canvas_set_color(canvas, ColorBlack);
+            canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+            //draw the bits of the barcode
+            x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+            //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern
+            if(i == 6) {
+                x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+            }
+        }
+    }
+
+    furi_string_free(left_structure);
+    furi_string_free(code_part);
+
+    //draw the ending guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+/**
+ * Draw a UPC-A barcode
+*/
+static void draw_upc_a(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* barcode_digits = barcode_data->correct_data;
+    BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+    int barcode_length = furi_string_size(barcode_digits);
+
+    int x = type_obj->start_pos;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+
+    //the guard patterns for the beginning, center, ending
+    char* end_bits = "101";
+    char* center_bits = "01010";
+
+    //draw the starting guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+    FuriString* code_part = furi_string_alloc();
+
+    //loop through each digit, find the encoding, and draw it
+    for(int i = 0; i < barcode_length; i++) {
+        char current_digit = furi_string_get_char(barcode_digits, i);
+        int index = current_digit - '0'; //convert the number into an int (also the index)
+
+        //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits
+        if(i <= 5) {
+            furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+        } else {
+            furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+        }
+
+        //convert the current_digit char into a string so it can be printed
+        char current_digit_string[2];
+        snprintf(current_digit_string, 2, "%c", current_digit);
+
+        //set the canvas color to black to print the digit
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+        //draw the bits of the barcode
+        x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+        //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern
+        if(i == 5) {
+            x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+        }
+    }
+
+    furi_string_free(code_part);
+
+    //draw the ending guard pattern
+    x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+static void draw_code_39(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* raw_data = barcode_data->raw_data;
+    FuriString* barcode_digits = barcode_data->correct_data;
+    //BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+    int barcode_length = furi_string_size(barcode_digits);
+    int total_pixels = 0;
+
+    for(int i = 0; i < barcode_length; i++) {
+        //1 for wide, 0 for narrow
+        char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+        int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+        if(wn_digit == 1) {
+            total_pixels += 3;
+        } else {
+            total_pixels += 1;
+        }
+        if((i + 1) % 9 == 0) {
+            total_pixels += 1;
+        }
+    }
+
+    int x = (128 - total_pixels) / 2;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+    bool filled_in = true;
+
+    //set the canvas color to black to print the digit
+    canvas_set_color(canvas, ColorBlack);
+    // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+    canvas_draw_str_aligned(
+        canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data));
+
+    for(int i = 0; i < barcode_length; i++) {
+        //1 for wide, 0 for narrow
+        char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+        int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+        if(filled_in) {
+            if(wn_digit == 1) {
+                x = draw_bits(canvas, "111", x, y, width, height);
+            } else {
+                x = draw_bits(canvas, "1", x, y, width, height);
+            }
+            filled_in = false;
+        } else {
+            if(wn_digit == 1) {
+                x = draw_bits(canvas, "000", x, y, width, height);
+            } else {
+                x = draw_bits(canvas, "0", x, y, width, height);
+            }
+            filled_in = true;
+        }
+        if((i + 1) % 9 == 0) {
+            x = draw_bits(canvas, "0", x, y, width, height);
+            filled_in = true;
+        }
+    }
+}
+
+static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* raw_data = barcode_data->raw_data;
+    FuriString* barcode_digits = barcode_data->correct_data;
+
+    int barcode_length = furi_string_size(barcode_digits);
+
+    int x = (128 - barcode_length) / 2;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+
+    x = draw_bits(canvas, furi_string_get_cstr(barcode_digits), x, y, width, height);
+
+    //set the canvas color to black to print the digit
+    canvas_set_color(canvas, ColorBlack);
+    // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+    canvas_draw_str_aligned(
+        canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data));
+}
+
+static void draw_codabar(Canvas* canvas, BarcodeData* barcode_data) {
+    FuriString* raw_data = barcode_data->raw_data;
+    FuriString* barcode_digits = barcode_data->correct_data;
+    //BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+    int barcode_length = furi_string_size(barcode_digits);
+    int total_pixels = 0;
+
+    for(int i = 0; i < barcode_length; i++) {
+        //1 for wide, 0 for narrow
+        char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+        int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+        if(wn_digit == 1) {
+            total_pixels += 3;
+        } else {
+            total_pixels += 1;
+        }
+        if((i + 1) % 7 == 0) {
+            total_pixels += 1;
+        }
+    }
+
+    int x = (128 - total_pixels) / 2;
+    int y = BARCODE_Y_START;
+    int width = 1;
+    int height = BARCODE_HEIGHT;
+    bool filled_in = true;
+
+    //set the canvas color to black to print the digit
+    canvas_set_color(canvas, ColorBlack);
+    // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+    canvas_draw_str_aligned(
+        canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data));
+
+    for(int i = 0; i < barcode_length; i++) {
+        //1 for wide, 0 for narrow
+        char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+        int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+        if(filled_in) {
+            if(wn_digit == 1) {
+                x = draw_bits(canvas, "111", x, y, width, height);
+            } else {
+                x = draw_bits(canvas, "1", x, y, width, height);
+            }
+            filled_in = false;
+        } else {
+            if(wn_digit == 1) {
+                x = draw_bits(canvas, "000", x, y, width, height);
+            } else {
+                x = draw_bits(canvas, "0", x, y, width, height);
+            }
+            filled_in = true;
+        }
+        if((i + 1) % 7 == 0) {
+            x = draw_bits(canvas, "0", x, y, width, height);
+            filled_in = true;
+        }
+    }
+}
+
+static void barcode_draw_callback(Canvas* canvas, void* ctx) {
+    furi_assert(ctx);
+    BarcodeModel* barcode_model = ctx;
+    BarcodeData* data = barcode_model->data;
+    // const char* barcode_digits =;
+
+    canvas_clear(canvas);
+    if(data->valid) {
+        switch(data->type_obj->type) {
+        case UPCA:
+            draw_upc_a(canvas, data);
+            break;
+        case EAN8:
+            draw_ean_8(canvas, data);
+            break;
+        case EAN13:
+            draw_ean_13(canvas, data);
+            break;
+        case CODE39:
+            draw_code_39(canvas, data);
+            break;
+        case CODE128:
+        case CODE128C:
+            draw_code_128(canvas, data);
+            break;
+        case CODABAR:
+            draw_codabar(canvas, data);
+            break;
+        case UNKNOWN:
+        default:
+            break;
+        }
+    } else {
+        switch(data->reason) {
+        case WrongNumberOfDigits:
+            draw_error_str(canvas, "Wrong # of characters");
+            break;
+        case InvalidCharacters:
+            draw_error_str(canvas, "Invalid characters");
+            break;
+        case UnsupportedType:
+            draw_error_str(canvas, "Unsupported barcode type");
+            break;
+        case FileOpening:
+            draw_error_str(canvas, "Could not open file");
+            break;
+        case InvalidFileData:
+            draw_error_str(canvas, "Invalid file data");
+            break;
+        case MissingEncodingTable:
+            draw_error_str(canvas, "Missing encoding table");
+            break;
+        case EncodingTableError:
+            draw_error_str(canvas, "Encoding table error");
+            break;
+        default:
+            draw_error_str(canvas, "Could not read barcode data");
+            break;
+        }
+    }
+}
+
+bool barcode_input_callback(InputEvent* input_event, void* ctx) {
+    UNUSED(ctx);
+    //furi_assert(ctx);
+
+    //Barcode* test_view_object = ctx;
+
+    if(input_event->key == InputKeyBack) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+Barcode* barcode_view_allocate(BarcodeApp* barcode_app) {
+    furi_assert(barcode_app);
+
+    Barcode* barcode = malloc(sizeof(Barcode));
+
+    barcode->view = view_alloc();
+    barcode->barcode_app = barcode_app;
+
+    view_set_context(barcode->view, barcode);
+    view_allocate_model(barcode->view, ViewModelTypeLocking, sizeof(BarcodeModel));
+    view_set_draw_callback(barcode->view, barcode_draw_callback);
+    view_set_input_callback(barcode->view, barcode_input_callback);
+
+    return barcode;
+}
+
+void barcode_free_model(Barcode* barcode) {
+    with_view_model(
+        barcode->view,
+        BarcodeModel * model,
+        {
+            if(model->file_path != NULL) {
+                furi_string_free(model->file_path);
+            }
+            if(model->data != NULL) {
+                if(model->data->raw_data != NULL) {
+                    furi_string_free(model->data->raw_data);
+                }
+                if(model->data->correct_data != NULL) {
+                    furi_string_free(model->data->correct_data);
+                }
+                free(model->data);
+            }
+        },
+        false);
+}
+
+void barcode_free(Barcode* barcode) {
+    furi_assert(barcode);
+
+    barcode_free_model(barcode);
+    view_free(barcode->view);
+    free(barcode);
+}
+
+View* barcode_get_view(Barcode* barcode) {
+    furi_assert(barcode);
+    return barcode->view;
+}

+ 23 - 0
barcode_gen/views/barcode_view.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef struct {
+    View* view;
+    BarcodeApp* barcode_app;
+} Barcode;
+
+typedef struct {
+    FuriString* file_path;
+    BarcodeData* data;
+} BarcodeModel;
+
+Barcode* barcode_view_allocate(BarcodeApp* barcode_app);
+
+void barcode_free_model(Barcode* barcode);
+
+void barcode_free(Barcode* barcode);
+
+View* barcode_get_view(Barcode* barcode);

+ 494 - 0
barcode_gen/views/create_view.c

@@ -0,0 +1,494 @@
+#include "../barcode_app.h"
+#include "create_view.h"
+#include <math.h>
+
+#define LINE_HEIGHT 16
+#define TEXT_PADDING 4
+#define TOTAL_MENU_ITEMS 5
+
+typedef enum {
+    TypeMenuItem,
+    FileNameMenuItem,
+    BarcodeDataMenuItem,
+    SaveMenuButton,
+    DeleteMenuButton
+} MenuItems;
+
+/**
+ * Took this function from blackjack
+ * @author @teeebor
+*/
+void draw_menu_item(
+    Canvas* const canvas,
+    const char* text,
+    const char* value,
+    int y,
+    bool left_caret,
+    bool right_caret,
+    bool selected) {
+    UNUSED(selected);
+    if(y < 0 || y >= 64) {
+        return;
+    }
+
+    if(selected) {
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
+        canvas_set_color(canvas, ColorWhite);
+    }
+
+    canvas_draw_str_aligned(canvas, 4, y + TEXT_PADDING, AlignLeft, AlignTop, text);
+    if(left_caret) {
+        canvas_draw_str_aligned(canvas, 60, y + TEXT_PADDING, AlignLeft, AlignTop, "<");
+    }
+
+    canvas_draw_str_aligned(canvas, 90, y + TEXT_PADDING, AlignCenter, AlignTop, value);
+    if(right_caret) {
+        canvas_draw_str_aligned(canvas, 120, y + TEXT_PADDING, AlignRight, AlignTop, ">");
+    }
+
+    canvas_set_color(canvas, ColorBlack);
+}
+
+void draw_button(Canvas* const canvas, const char* text, int y, bool selected) {
+    if(selected) {
+        canvas_set_color(canvas, ColorBlack);
+        canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
+        canvas_set_color(canvas, ColorWhite);
+    }
+
+    canvas_draw_str_aligned(canvas, 64, y + TEXT_PADDING, AlignCenter, AlignTop, text);
+
+    canvas_set_color(canvas, ColorBlack);
+}
+
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+    furi_assert(ctx);
+
+    CreateViewModel* create_view_model = ctx;
+
+    BarcodeTypeObj* type_obj = create_view_model->barcode_type;
+    if(create_view_model->barcode_type == NULL) {
+        return;
+    }
+    BarcodeType selected_type = type_obj->type;
+
+    int selected_menu_item = create_view_model->selected_menu_item;
+
+    int total_menu_items = create_view_model->mode == EditMode ? TOTAL_MENU_ITEMS :
+                                                                 TOTAL_MENU_ITEMS - 1;
+
+    int startY = 0;
+
+    //the menu items index that is/would be in view
+    //int current_last_menu_item = selected_menu_item + 3;
+    if(selected_menu_item > 1) {
+        int offset = 2;
+        if(selected_menu_item + offset > total_menu_items) {
+            offset = 3;
+        }
+        startY -= (LINE_HEIGHT * (selected_menu_item - offset));
+    }
+
+    //ensure that the scroll height is atleast 1
+    int scrollHeight = ceil(64.0 / total_menu_items);
+    int scrollPos = scrollHeight * selected_menu_item;
+
+    canvas_set_color(canvas, ColorBlack);
+    //draw the scroll bar box
+    canvas_draw_box(canvas, 125, scrollPos, 3, scrollHeight);
+    //draw the scroll bar track
+    canvas_draw_box(canvas, 126, 0, 1, 64);
+
+    draw_menu_item(
+        canvas,
+        "Type",
+        type_obj->name,
+        TypeMenuItem * LINE_HEIGHT + startY,
+        selected_type > 0,
+        selected_type < NUMBER_OF_BARCODE_TYPES - 2,
+        selected_menu_item == TypeMenuItem);
+
+    draw_menu_item(
+        canvas,
+        "Name",
+        furi_string_empty(create_view_model->file_name) ?
+            "--" :
+            furi_string_get_cstr(create_view_model->file_name),
+        FileNameMenuItem * LINE_HEIGHT + startY,
+        false,
+        false,
+        selected_menu_item == FileNameMenuItem);
+
+    draw_menu_item(
+        canvas,
+        "Data",
+        furi_string_empty(create_view_model->barcode_data) ?
+            "--" :
+            furi_string_get_cstr(create_view_model->barcode_data),
+        BarcodeDataMenuItem * LINE_HEIGHT + startY,
+        false,
+        false,
+        selected_menu_item == BarcodeDataMenuItem);
+
+    draw_button(
+        canvas,
+        "Save",
+        SaveMenuButton * LINE_HEIGHT + startY,
+        selected_menu_item == SaveMenuButton);
+
+    if(create_view_model->mode == EditMode) {
+        draw_button(
+            canvas,
+            "Delete",
+            DeleteMenuButton * LINE_HEIGHT + startY,
+            selected_menu_item == DeleteMenuButton);
+    }
+}
+
+void text_input_callback(void* ctx) {
+    CreateView* create_view_object = ctx;
+
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            if(create_view_object->setter == FileNameSetter) {
+                furi_string_set_str(model->file_name, create_view_object->input);
+            }
+            if(create_view_object->setter == BarcodeDataSetter) {
+                furi_string_set_str(model->barcode_data, create_view_object->input);
+            }
+        },
+        true);
+
+    view_dispatcher_switch_to_view(
+        create_view_object->barcode_app->view_dispatcher, CreateBarcodeView);
+}
+
+static bool app_input_callback(InputEvent* input_event, void* ctx) {
+    furi_assert(ctx);
+
+    if(input_event->key == InputKeyBack) {
+        return false;
+    }
+
+    CreateView* create_view_object = ctx;
+
+    //get the currently selected menu item from the model
+    int selected_menu_item = 0;
+    BarcodeTypeObj* barcode_type = NULL;
+    FuriString* file_name;
+    FuriString* barcode_data;
+    CreateMode mode;
+
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            selected_menu_item = model->selected_menu_item;
+            barcode_type = model->barcode_type;
+            file_name = model->file_name;
+            barcode_data = model->barcode_data;
+            mode = model->mode;
+        },
+        true);
+
+    int total_menu_items = mode == EditMode ? TOTAL_MENU_ITEMS : TOTAL_MENU_ITEMS - 1;
+
+    if(input_event->type == InputTypePress) {
+        if(input_event->key == InputKeyUp && selected_menu_item > 0) {
+            selected_menu_item--;
+        } else if(input_event->key == InputKeyDown && selected_menu_item < total_menu_items - 1) {
+            selected_menu_item++;
+        } else if(input_event->key == InputKeyLeft) {
+            if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
+                if(barcode_type->type > 0) {
+                    barcode_type = barcode_type_objs[barcode_type->type - 1];
+                }
+            }
+        } else if(input_event->key == InputKeyRight) {
+            if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
+                if(barcode_type->type < NUMBER_OF_BARCODE_TYPES - 2) {
+                    barcode_type = barcode_type_objs[barcode_type->type + 1];
+                }
+            }
+        } else if(input_event->key == InputKeyOk) {
+            if(selected_menu_item == FileNameMenuItem && barcode_type != NULL) {
+                create_view_object->setter = FileNameSetter;
+
+                snprintf(
+                    create_view_object->input,
+                    sizeof(create_view_object->input),
+                    "%s",
+                    furi_string_get_cstr(file_name));
+
+                text_input_set_result_callback(
+                    create_view_object->barcode_app->text_input,
+                    text_input_callback,
+                    create_view_object,
+                    create_view_object->input,
+                    TEXT_BUFFER_SIZE - BARCODE_EXTENSION_LENGTH, //remove the barcode length
+                    //clear default text
+                    false);
+                text_input_set_header_text(
+                    create_view_object->barcode_app->text_input, "File Name");
+
+                view_dispatcher_switch_to_view(
+                    create_view_object->barcode_app->view_dispatcher, TextInputView);
+            }
+            if(selected_menu_item == BarcodeDataMenuItem && barcode_type != NULL) {
+                create_view_object->setter = BarcodeDataSetter;
+
+                snprintf(
+                    create_view_object->input,
+                    sizeof(create_view_object->input),
+                    "%s",
+                    furi_string_get_cstr(barcode_data));
+
+                text_input_set_result_callback(
+                    create_view_object->barcode_app->text_input,
+                    text_input_callback,
+                    create_view_object,
+                    create_view_object->input,
+                    TEXT_BUFFER_SIZE,
+                    //clear default text
+                    false);
+                text_input_set_header_text(
+                    create_view_object->barcode_app->text_input, "Barcode Data");
+
+                view_dispatcher_switch_to_view(
+                    create_view_object->barcode_app->view_dispatcher, TextInputView);
+            }
+            if(selected_menu_item == SaveMenuButton && barcode_type != NULL) {
+                save_barcode(create_view_object);
+            }
+            if(selected_menu_item == DeleteMenuButton && barcode_type != NULL) {
+                if(mode == EditMode) {
+                    remove_barcode(create_view_object);
+                } else if(mode == NewMode) {
+                    view_dispatcher_switch_to_view(
+                        create_view_object->barcode_app->view_dispatcher, MainMenuView);
+                }
+            }
+        }
+    }
+
+    //change the currently selected menu item
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            model->selected_menu_item = selected_menu_item;
+            model->barcode_type = barcode_type;
+        },
+        true);
+
+    return true;
+}
+
+CreateView* create_view_allocate(BarcodeApp* barcode_app) {
+    furi_assert(barcode_app);
+
+    CreateView* create_view_object = malloc(sizeof(CreateView));
+
+    create_view_object->view = view_alloc();
+    create_view_object->barcode_app = barcode_app;
+
+    view_set_context(create_view_object->view, create_view_object);
+    view_allocate_model(create_view_object->view, ViewModelTypeLocking, sizeof(CreateViewModel));
+    view_set_draw_callback(create_view_object->view, app_draw_callback);
+    view_set_input_callback(create_view_object->view, app_input_callback);
+
+    return create_view_object;
+}
+
+void create_view_free_model(CreateView* create_view_object) {
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            if(model->file_path != NULL) {
+                furi_string_free(model->file_path);
+            }
+            if(model->file_name != NULL) {
+                furi_string_free(model->file_name);
+            }
+            if(model->barcode_data != NULL) {
+                furi_string_free(model->barcode_data);
+            }
+        },
+        true);
+}
+
+void remove_barcode(CreateView* create_view_object) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    bool success = false;
+
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            FURI_LOG_I(TAG, "Attempting to remove file");
+            if(model->file_path != NULL) {
+                FURI_LOG_I(TAG, "Removing File: %s", furi_string_get_cstr(model->file_path));
+                if(storage_simply_remove(storage, furi_string_get_cstr(model->file_path))) {
+                    FURI_LOG_I(
+                        TAG,
+                        "File: \"%s\" was successfully removed",
+                        furi_string_get_cstr(model->file_path));
+                    success = true;
+                } else {
+                    FURI_LOG_E(TAG, "Unable to remove file!");
+                    success = false;
+                }
+            } else {
+                FURI_LOG_E(TAG, "Could not remove barcode file");
+                success = false;
+            }
+        },
+        true);
+    furi_record_close(RECORD_STORAGE);
+
+    with_view_model(
+        create_view_object->barcode_app->message_view->view,
+        MessageViewModel * model,
+        {
+            if(success) {
+                model->message = "File Deleted";
+            } else {
+                model->message = "Could not delete file";
+            }
+        },
+        true);
+
+    view_dispatcher_switch_to_view(
+        create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+}
+
+void save_barcode(CreateView* create_view_object) {
+    BarcodeTypeObj* barcode_type = NULL;
+    FuriString* file_path; //this may be empty
+    FuriString* file_name;
+    FuriString* barcode_data;
+    CreateMode mode;
+
+    with_view_model(
+        create_view_object->view,
+        CreateViewModel * model,
+        {
+            file_path = model->file_path;
+            file_name = model->file_name;
+            barcode_data = model->barcode_data;
+            barcode_type = model->barcode_type;
+            mode = model->mode;
+        },
+        true);
+
+    if(file_name == NULL || furi_string_empty(file_name)) {
+        FURI_LOG_E(TAG, "File Name cannot be empty");
+        return;
+    }
+    if(barcode_data == NULL || furi_string_empty(barcode_data)) {
+        FURI_LOG_E(TAG, "Barcode Data cannot be empty");
+        return;
+    }
+    if(barcode_type == NULL) {
+        FURI_LOG_E(TAG, "Type not defined");
+        return;
+    }
+
+    bool success = false;
+
+    FuriString* full_file_path = furi_string_alloc_set(DEFAULT_USER_BARCODES);
+    furi_string_push_back(full_file_path, '/');
+    furi_string_cat(full_file_path, file_name);
+    furi_string_cat_str(full_file_path, BARCODE_EXTENSION);
+
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    if(mode == EditMode) {
+        if(!furi_string_empty(file_path)) {
+            if(!furi_string_equal(file_path, full_file_path)) {
+                FS_Error error = storage_common_rename(
+                    storage,
+                    furi_string_get_cstr(file_path),
+                    furi_string_get_cstr(full_file_path));
+                if(error != FSE_OK) {
+                    FURI_LOG_E(TAG, "Rename error: %s", storage_error_get_desc(error));
+                } else {
+                    FURI_LOG_I(TAG, "Rename Success");
+                }
+            }
+        }
+    }
+
+    FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+    FURI_LOG_I(TAG, "Saving Barcode to: %s", furi_string_get_cstr(full_file_path));
+
+    bool file_opened_status = false;
+    if(mode == NewMode) {
+        file_opened_status =
+            flipper_format_file_open_new(ff, furi_string_get_cstr(full_file_path));
+    } else if(mode == EditMode) {
+        file_opened_status =
+            flipper_format_file_open_always(ff, furi_string_get_cstr(full_file_path));
+    }
+
+    if(file_opened_status) {
+        // Filetype: Barcode
+        // Version: 1
+
+        // # Types - UPC-A, EAN-8, EAN-13, CODE-39
+        // Type: CODE-39
+        // Data: AB
+        flipper_format_write_string_cstr(ff, "Filetype", "Barcode");
+
+        flipper_format_write_string_cstr(ff, "Version", FILE_VERSION);
+
+        flipper_format_write_comment_cstr(
+            ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128, Codabar");
+
+        flipper_format_write_string_cstr(ff, "Type", barcode_type->name);
+
+        flipper_format_write_string_cstr(ff, "Data", furi_string_get_cstr(barcode_data));
+
+        success = true;
+    } else {
+        FURI_LOG_E(TAG, "Save error");
+        success = false;
+    }
+    furi_string_free(full_file_path);
+    flipper_format_free(ff);
+    furi_record_close(RECORD_STORAGE);
+
+    with_view_model(
+        create_view_object->barcode_app->message_view->view,
+        MessageViewModel * model,
+        {
+            if(success) {
+                model->message = "File Saved!";
+            } else {
+                model->message = "A saving error has occurred";
+            }
+        },
+        true);
+
+    view_dispatcher_switch_to_view(
+        create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+}
+
+void create_view_free(CreateView* create_view_object) {
+    furi_assert(create_view_object);
+
+    create_view_free_model(create_view_object);
+    view_free(create_view_object->view);
+    free(create_view_object);
+}
+
+View* create_get_view(CreateView* create_view_object) {
+    furi_assert(create_view_object);
+    return create_view_object->view;
+}

+ 46 - 0
barcode_gen/views/create_view.h

@@ -0,0 +1,46 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef enum {
+    FileNameSetter,
+    BarcodeDataSetter
+} InputSetter; //what value to set for the text input view
+
+typedef enum {
+    EditMode,
+
+    NewMode
+} CreateMode;
+
+typedef struct {
+    View* view;
+    BarcodeApp* barcode_app;
+
+    InputSetter setter;
+    char input[TEXT_BUFFER_SIZE];
+} CreateView;
+
+typedef struct {
+    int selected_menu_item;
+
+    CreateMode mode;
+    BarcodeTypeObj* barcode_type;
+    FuriString* file_path; //the current file that is opened
+    FuriString* file_name;
+    FuriString* barcode_data;
+} CreateViewModel;
+
+CreateView* create_view_allocate(BarcodeApp* barcode_app);
+
+void remove_barcode(CreateView* create_view_object);
+
+void save_barcode(CreateView* create_view_object);
+
+void create_view_free_model(CreateView* create_view_object);
+
+void create_view_free(CreateView* create_view_object);
+
+View* create_get_view(CreateView* create_view_object);

+ 66 - 0
barcode_gen/views/message_view.c

@@ -0,0 +1,66 @@
+#include "../barcode_app.h"
+#include "message_view.h"
+
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+    furi_assert(ctx);
+
+    MessageViewModel* message_view_model = ctx;
+
+    canvas_clear(canvas);
+    if(message_view_model->message != NULL) {
+        canvas_draw_str_aligned(
+            canvas, 62, 30, AlignCenter, AlignCenter, message_view_model->message);
+    }
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_box(canvas, 100, 52, 28, 12);
+    canvas_set_color(canvas, ColorWhite);
+    canvas_draw_str_aligned(canvas, 114, 58, AlignCenter, AlignCenter, "OK");
+}
+
+static bool app_input_callback(InputEvent* input_event, void* ctx) {
+    furi_assert(ctx);
+
+    MessageView* message_view_object = ctx;
+
+    if(input_event->key == InputKeyBack) {
+        view_dispatcher_switch_to_view(
+            message_view_object->barcode_app->view_dispatcher, MainMenuView);
+    }
+    if(input_event->type == InputTypeShort) {
+        if(input_event->key == InputKeyOk) {
+            view_dispatcher_switch_to_view(
+                message_view_object->barcode_app->view_dispatcher, MainMenuView);
+        }
+    }
+
+    return true;
+}
+
+MessageView* message_view_allocate(BarcodeApp* barcode_app) {
+    furi_assert(barcode_app);
+
+    MessageView* message_view_object = malloc(sizeof(MessageView));
+
+    message_view_object->view = view_alloc();
+    message_view_object->barcode_app = barcode_app;
+
+    view_set_context(message_view_object->view, message_view_object);
+    view_allocate_model(message_view_object->view, ViewModelTypeLocking, sizeof(MessageViewModel));
+    view_set_draw_callback(message_view_object->view, app_draw_callback);
+    view_set_input_callback(message_view_object->view, app_input_callback);
+
+    return message_view_object;
+}
+
+void message_view_free(MessageView* message_view_object) {
+    furi_assert(message_view_object);
+
+    view_free(message_view_object->view);
+    free(message_view_object);
+}
+
+View* message_get_view(MessageView* message_view_object) {
+    furi_assert(message_view_object);
+    return message_view_object->view;
+}

+ 22 - 0
barcode_gen/views/message_view.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef struct {
+    View* view;
+    BarcodeApp* barcode_app;
+} MessageView;
+
+typedef struct {
+    const char* message;
+} MessageViewModel;
+
+MessageView* message_view_allocate(BarcodeApp* barcode_app);
+
+void message_view_free_model(MessageView* message_view_object);
+
+void message_view_free(MessageView* message_view_object);
+
+View* message_get_view(MessageView* message_view_object);