Files
ESLPwn/tagtinker_app.h
T
HoggorminoandClaude Opus 4.8 0447f12164 Add a custom-size option for unrecognized infrared tags
A tag whose type code is not in the profile table has no dimensions, so
after the unknown-profile guard its Set Text / Set Image / WiFi Plugins
actions are hidden. This adds a "Set Custom Size" action for those tags:
enter width, height and colour, and the graphics actions become usable.

The IR framing is identical to a table profile; only the screen geometry
differs, so no protocol is guessed. Custom size is applied only when the
barcode's type is not in the table, so a real profile is never overridden.

- New scene (VariableItemList): Width and Height in 8 px steps from 8 to
  800, Colour mono/red/yellow, and Save.
- tagtinker_target_refresh_profile() synthesizes a "Custom" profile from
  the stored size for unknown-type targets, so Show Tag Info, Set Text
  and Set Image all see real dimensions.
- targets.txt gains three optional fields (barcode|name|cw|ch|ccolor);
  older two-field lines still load, with no custom size.
- "Set Custom Size" appears in Target Actions only for unknown-type
  targets (tagtinker_type_is_known()).

Builds clean on release SDK 1.4.3 (API 87.1) and dev SDK; no new
clang-format drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-16 21:58:55 +02:00

344 lines
10 KiB
C

/*
* App state.
*/
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/view.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/text_input.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/popup.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_box.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <storage/storage.h>
#include <bt/bt_service/bt.h>
#include <targets/f7/ble_glue/profiles/serial_profile.h>
#include <nfc/nfc.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include "views/numlock_input.h"
#include "scenes/tagtinker_scene.h"
#include "protocol/tagtinker_proto.h"
#include "ir/tagtinker_ir.h"
#include "nfc/tagtinker_nfc.h"
#define TAGTINKER_TAG "TagTinker"
#define TAGTINKER_DISPLAY_NAME "TagTinker"
#define TAGTINKER_VERSION "2.1"
#define TAGTINKER_BC_LEN 17
#define TAGTINKER_HEX_LEN 64
#define TAGTINKER_MAX_TARGETS 16
#define TAGTINKER_TARGET_NAME_LEN 16
#define TAGTINKER_MAX_PRESETS 6
#define TAGTINKER_MAX_SYNCED_IMAGES 24
#define TAGTINKER_PRESET_TEXT_LEN 32
#define TAGTINKER_IMAGE_PATH_LEN 255
#define TAGTINKER_SYNC_JOB_ID_LEN 32
typedef enum {
TagTinkerTxModeNone = 0,
TagTinkerTxModeTextImage,
TagTinkerTxModeBmpImage,
} TagTinkerTxMode;
typedef enum {
TagTinkerSignalPP4 = 0,
} TagTinkerSignalMode;
typedef struct {
TagTinkerTxMode mode;
uint8_t plid[4];
uint8_t page;
uint16_t width;
uint16_t height;
uint16_t pos_x;
uint16_t pos_y;
char image_path[TAGTINKER_IMAGE_PATH_LEN + 1];
} TagTinkerImageTxJob;
typedef struct {
char job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
char barcode[TAGTINKER_BC_LEN + 1];
uint16_t width;
uint16_t height;
uint8_t page;
/* True when this BMP was authored for a different resolution than the
* current target, meaning the transmitter will rescale it on the fly. */
bool resampled;
char image_path[TAGTINKER_IMAGE_PATH_LEN + 1];
} TagTinkerSyncedImage;
typedef enum {
TagTinkerTextInputNewText = 0,
TagTinkerTextInputKeepText = 1,
TagTinkerTextInputRenameTarget = 2,
} TagTinkerTextInputMode;
/* Views */
typedef enum {
TagTinkerViewSubmenu,
TagTinkerViewVarItemList,
TagTinkerViewTextInput,
TagTinkerViewPopup,
TagTinkerViewWidget,
TagTinkerViewNumlock,
TagTinkerViewTextBox,
TagTinkerViewTargetActions,
TagTinkerViewWarning,
TagTinkerViewTransmit,
TagTinkerViewAbout,
} TagTinkerView;
/* Saved ESL target */
typedef struct {
char name[TAGTINKER_TARGET_NAME_LEN + 1];
char barcode[TAGTINKER_BC_LEN + 1];
uint8_t plid[4];
TagTinkerTagProfile profile;
/* User-entered geometry for a tag whose type code is not in the profile
* table. 0x0 means unset. Applied by tagtinker_target_refresh_profile only
* when the barcode's type is unknown, so a real profile is never overridden. */
uint16_t custom_width;
uint16_t custom_height;
uint8_t custom_color; /* TagTinkerTagColor */
} TagTinkerTarget;
struct TagTinkerApp {
/* GUI */
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
/* Views */
Submenu* submenu;
VariableItemList* var_item_list;
TextInput* text_input;
Popup* popup;
Widget* widget;
NumlockInput* numlock;
TextBox* text_box;
View* target_actions_view;
View* warning_view;
View* transmit_view;
View* about_view;
bool warning_view_allocated;
bool transmit_view_allocated;
bool about_view_allocated;
/* TX state */
bool tx_active;
FuriThread* tx_thread;
/* NFC scan state */
Nfc* nfc;
FuriThread* nfc_thread;
volatile bool nfc_scanning;
/* Broadcast settings */
uint8_t broadcast_type;
uint8_t page;
uint16_t duration;
uint16_t repeats;
bool forever;
bool tx_spam;
/* Current target */
char barcode[TAGTINKER_BC_LEN + 1];
uint8_t plid[4];
bool barcode_valid;
int8_t selected_target; /* -1 = none */
/* Saved targets */
TagTinkerTarget targets[TAGTINKER_MAX_TARGETS];
/* Custom-size scene working values. */
uint16_t custom_size_w;
uint16_t custom_size_h;
uint8_t custom_size_color;
uint8_t target_count;
/* Text to push */
char text_input_buf[64];
/* ESL display size for current target */
uint16_t esl_width;
uint16_t esl_height;
/* Frame buffer */
uint8_t frame_buf[TAGTINKER_MAX_FRAME_SIZE];
size_t frame_len;
/* Multi-frame sequence */
uint8_t** frame_sequence;
size_t* frame_lengths;
uint16_t* frame_repeats;
size_t frame_seq_count;
bool invert_text;
bool color_clear;
uint8_t text_padding_pct;
/* Saved recents */
struct {
uint16_t width;
uint16_t height;
uint8_t page;
bool invert;
bool color_clear;
uint8_t padding;
uint8_t signal_mode;
char text[TAGTINKER_PRESET_TEXT_LEN];
} recents[TAGTINKER_MAX_PRESETS];
uint8_t recent_count;
TagTinkerSyncedImage synced_images[TAGTINKER_MAX_SYNCED_IMAGES];
uint8_t synced_image_count;
/* Image settings */
uint8_t img_page;
uint16_t draw_x;
uint16_t draw_y;
uint16_t draw_width;
uint16_t draw_height;
TagTinkerImageTxJob image_tx_job;
TagTinkerCompressionMode compression_mode;
uint8_t data_frame_repeats;
TagTinkerSignalMode signal_mode;
bool show_startup_warning;
/* Indicates which mode triggered raw cmd (0=broadcast, 1=targeted) */
uint8_t raw_mode;
/* Browser BLE sync state */
Bt* bt;
FuriHalBleProfileBase* ble_serial;
BtStatus ble_status;
bool ble_sync_active;
bool ble_sync_start_pending;
bool ble_serial_configured;
uint32_t ble_total_rx;
uint8_t ble_last_bytes[3];
bool ble_sync_auto_send_pending;
uint8_t ble_sync_auto_send_delay;
uint16_t ble_synced_lines;
char ble_status_text[32];
char ble_rx_line[1024];
char ble_rx_pending_line[1024];
uint16_t ble_rx_len;
bool ble_rx_pending_ready;
bool ble_sync_job_active;
char ble_sync_job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
char ble_sync_barcode[TAGTINKER_BC_LEN + 1];
char ble_sync_temp_path[TAGTINKER_IMAGE_PATH_LEN + 1];
char ble_sync_final_path[TAGTINKER_IMAGE_PATH_LEN + 1];
char ble_sync_last_job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
uint32_t ble_sync_expected_bytes;
uint32_t ble_sync_received_bytes;
uint16_t ble_sync_last_chunk;
File* ble_sync_file;
uint16_t ble_sync_last_completed_chunks;
bool ble_sync_compact_protocol;
bool ble_sync_last_compact_protocol;
int8_t ble_sync_ready_target;
/* ---- WiFi Plugins (ESP32 dev board) -------------------------------- */
/* Opaque handle (TagTinkerWifi*) - declared in wifi/tagtinker_wifi.h.
* Stored as void* here so this header doesn't pull in expansion/serial
* deps for unrelated translation units. */
void* wifi;
/* WiFi link state mirrored from the ESP. */
uint8_t wifi_link_state; /* TT_WIFI_* */
int8_t wifi_rssi;
char wifi_ssid[33];
char wifi_ip[20];
char wifi_creds_ssid[33]; /* used by setup scene before sending */
char wifi_creds_pwd[65];
/* Plugin discovery cache. Up to TT_WIFI_MAX_FAP_PLUGINS slots. */
void* wifi_plugins; /* TagTinkerWifiPlugin[TT_WIFI_MAX_FAP_PLUGINS], heap-alloced */
uint8_t wifi_plugin_count;
bool wifi_plugins_loading;
int8_t wifi_selected_plugin;
/* Per-run state. */
char wifi_progress_msg[64];
uint8_t wifi_progress_pct;
char wifi_last_error[80];
bool wifi_run_in_flight;
bool wifi_result_ready;
/* Param values being collected by the run scene; one slot per plugin
* param, holding the textual value the user picked (string for STRING,
* stringified int for INT, option name for ENUM, "0"/"1" for BOOL). */
char wifi_param_values[6][64];
};
/* Main menu items */
typedef enum {
TagTinkerMenuBroadcast,
TagTinkerMenuTargetESL,
TagTinkerMenuWifiPlugins,
TagTinkerMenuAbout,
} TagTinkerMainMenuItem;
/* Broadcast menu items */
typedef enum {
TagTinkerBroadcastFlipPage,
TagTinkerBroadcastDebugScreen,
} TagTinkerBroadcastMenuItem;
/* Target action items */
typedef enum {
TagTinkerTargetDetails,
TagTinkerTargetRename,
TagTinkerTargetPushText,
TagTinkerTargetPushSyncedImage,
TagTinkerTargetWifiPlugins,
TagTinkerTargetDeleteSyncedImages,
TagTinkerTargetPingFlash,
TagTinkerTargetDeleteTag,
TagTinkerTargetCustomSize,
} TagTinkerTargetActionItem;
void tagtinker_target_refresh_profile(TagTinkerTarget* target);
void tagtinker_target_set_default_name(TagTinkerApp* app, TagTinkerTarget* target);
bool tagtinker_target_supports_graphics(const TagTinkerTarget* target);
bool tagtinker_target_supports_accent(const TagTinkerTarget* target);
const char* tagtinker_profile_kind_label(TagTinkerTagKind kind);
const char* tagtinker_profile_color_label(TagTinkerTagColor color);
int8_t tagtinker_find_target_by_barcode(const TagTinkerApp* app, const char* barcode);
int8_t tagtinker_ensure_target(TagTinkerApp* app, const char* barcode);
bool tagtinker_find_latest_synced_image(
const TagTinkerApp* app,
const char* barcode,
TagTinkerSyncedImage* image);
size_t tagtinker_delete_synced_images_for_barcode(TagTinkerApp* app, const char* barcode);
bool tagtinker_delete_target(TagTinkerApp* app, uint8_t index);
void tagtinker_free_frame_sequence(TagTinkerApp* app);
uint16_t tagtinker_pick_chunk_height(uint16_t width, bool color_clear);
void tagtinker_prepare_text_tx(TagTinkerApp* app, const uint8_t plid[4]);
void tagtinker_prepare_bmp_tx(
TagTinkerApp* app,
const uint8_t plid[4],
const char* image_path,
uint16_t width,
uint16_t height,
uint8_t page);
void tagtinker_select_target(TagTinkerApp* app, uint8_t index);
void tagtinker_settings_load(TagTinkerApp* app);
bool tagtinker_settings_save(const TagTinkerApp* app);
void tagtinker_targets_load(TagTinkerApp* app);
bool tagtinker_targets_save(const TagTinkerApp* app);
void tagtinker_recents_load(TagTinkerApp* app);
bool tagtinker_recents_save(const TagTinkerApp* app);
void tagtinker_recents_add(TagTinkerApp* app, const char* text);