mirror of
https://github.com/i12bp8/ESLPwn.git
synced 2026-09-27 05:37:56 +00:00
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>
206 lines
6.1 KiB
C
206 lines
6.1 KiB
C
/*
|
|
* ESL protocol helpers.
|
|
*
|
|
* This layer turns barcodes, pixels, and payload bytes into the frames
|
|
* that the Flipper sends over IR to the tag.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
|
|
#define TAGTINKER_PROTO_DM 0x85
|
|
#define TAGTINKER_PROTO_SEG 0x84
|
|
#define TAGTINKER_MAX_FRAME_SIZE 96
|
|
#define TAGTINKER_IMAGE_DATA_BYTES_PER_FRAME 20U
|
|
|
|
/* Color 2.6 glass is landscape 296x152. Image header is 152x296 (short x long). */
|
|
#define TAGTINKER_TYPE_SMARTAG_COLOR_26 1626
|
|
#define TAGTINKER_COLOR26_WIRE_W 152U
|
|
#define TAGTINKER_COLOR26_WIRE_H 296U
|
|
#define TAGTINKER_COLOR26_GLASS_W 296U
|
|
#define TAGTINKER_COLOR26_GLASS_H 152U
|
|
|
|
static inline bool tagtinker_type_needs_wh_swap(uint16_t type_code) {
|
|
return type_code == TAGTINKER_TYPE_SMARTAG_COLOR_26;
|
|
}
|
|
|
|
static inline bool tagtinker_type_uses_ui_page(uint16_t type_code) {
|
|
return tagtinker_type_needs_wh_swap(type_code);
|
|
}
|
|
|
|
/* Store-used Color 2.6 tags keep the barcode on page 1. Page 2 is the image
|
|
* slot. Unspecified pages (0 or 1) map there. Explicit 2-7 are left alone. */
|
|
static inline uint8_t tagtinker_color26_resolve_page(uint8_t page) {
|
|
if(page <= 1U) return 2U;
|
|
if(page > 7U) return 7U;
|
|
return page;
|
|
}
|
|
|
|
/* Wire (px, py) on 152x296 -> glass (bx, by) 296x152. */
|
|
static inline void tagtinker_color26_proto_to_glass(
|
|
uint16_t proto_w,
|
|
uint16_t px,
|
|
uint16_t py,
|
|
uint16_t* bx,
|
|
uint16_t* by) {
|
|
*bx = py;
|
|
*by = (uint16_t)(proto_w - 1U - px);
|
|
}
|
|
|
|
typedef struct TagTinkerApp TagTinkerApp;
|
|
|
|
/* CRC used by the ESL wire format. */
|
|
uint16_t tagtinker_crc16(const uint8_t* data, size_t len);
|
|
|
|
typedef enum {
|
|
TagTinkerTagKindUnknown = 0,
|
|
TagTinkerTagKindDotMatrix,
|
|
TagTinkerTagKindSegment,
|
|
} TagTinkerTagKind;
|
|
|
|
typedef enum {
|
|
TagTinkerTagColorMono = 0,
|
|
TagTinkerTagColorRed,
|
|
TagTinkerTagColorYellow,
|
|
} TagTinkerTagColor;
|
|
|
|
typedef struct {
|
|
uint16_t type_code;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
TagTinkerTagKind kind;
|
|
TagTinkerTagColor color;
|
|
const char* model_name;
|
|
uint8_t pl_bit_def;
|
|
bool known;
|
|
} TagTinkerTagProfile;
|
|
|
|
static inline bool tagtinker_profile_needs_wh_swap(const TagTinkerTagProfile* profile) {
|
|
return profile && tagtinker_type_needs_wh_swap(profile->type_code);
|
|
}
|
|
|
|
static inline bool tagtinker_profile_uses_ui_page(const TagTinkerTagProfile* profile) {
|
|
return profile && tagtinker_type_uses_ui_page(profile->type_code);
|
|
}
|
|
|
|
static inline void tagtinker_profile_glass_size(
|
|
const TagTinkerTagProfile* profile,
|
|
uint16_t* width,
|
|
uint16_t* height) {
|
|
if(!profile || !width || !height) return;
|
|
if(tagtinker_profile_needs_wh_swap(profile)) {
|
|
*width = TAGTINKER_COLOR26_GLASS_W;
|
|
*height = TAGTINKER_COLOR26_GLASS_H;
|
|
return;
|
|
}
|
|
*width = profile->width;
|
|
*height = profile->height;
|
|
}
|
|
|
|
bool tagtinker_is_barcode_valid(const char* barcode);
|
|
bool tagtinker_barcode_to_plid(const char* barcode, uint8_t plid[4]);
|
|
bool tagtinker_barcode_to_type(const char* barcode, uint16_t* type_code);
|
|
|
|
/* True when this type code has an entry in the built-in profile table. */
|
|
bool tagtinker_type_is_known(uint16_t type_code);
|
|
|
|
/* Bounds for a user-entered custom tag size (pixels). Width is byte-aligned. */
|
|
#define TAGTINKER_CUSTOM_SIZE_MIN 8U
|
|
#define TAGTINKER_CUSTOM_SIZE_MAX 800U
|
|
#define TAGTINKER_CUSTOM_SIZE_STEP 8U
|
|
bool tagtinker_barcode_to_profile(const char* barcode, TagTinkerTagProfile* profile);
|
|
|
|
typedef struct {
|
|
uint8_t* data;
|
|
size_t byte_count;
|
|
uint8_t comp_type;
|
|
} TagTinkerImagePayload;
|
|
|
|
typedef enum {
|
|
TagTinkerCompressionAuto = 0,
|
|
TagTinkerCompressionRaw,
|
|
TagTinkerCompressionRle,
|
|
} TagTinkerCompressionMode;
|
|
|
|
bool tagtinker_encode_image_payload(
|
|
const uint8_t* pixels,
|
|
uint16_t width,
|
|
uint16_t height,
|
|
bool color_clear,
|
|
TagTinkerCompressionMode mode,
|
|
TagTinkerImagePayload* payload);
|
|
|
|
typedef uint8_t (*TagTinkerPixelAtFn)(size_t idx, void* ctx);
|
|
|
|
bool tagtinker_encode_fn_payload(
|
|
TagTinkerPixelAtFn pixel_at,
|
|
void* ctx,
|
|
size_t total_pixels,
|
|
TagTinkerCompressionMode mode,
|
|
TagTinkerImagePayload* payload);
|
|
|
|
bool tagtinker_encode_planes_payload(
|
|
const uint8_t* primary_pixels,
|
|
const uint8_t* secondary_pixels,
|
|
size_t pixel_count,
|
|
TagTinkerCompressionMode mode,
|
|
TagTinkerImagePayload* payload);
|
|
void tagtinker_free_image_payload(TagTinkerImagePayload* payload);
|
|
size_t tagtinker_make_image_param_frame(
|
|
uint8_t* buf,
|
|
const uint8_t plid[4],
|
|
uint16_t byte_count,
|
|
uint8_t comp_type,
|
|
uint8_t page,
|
|
uint16_t width,
|
|
uint16_t height,
|
|
uint16_t pos_x,
|
|
uint16_t pos_y);
|
|
size_t tagtinker_make_image_data_frame(
|
|
uint8_t* buf,
|
|
const uint8_t plid[4],
|
|
uint16_t frame_index,
|
|
const uint8_t data_bytes[TAGTINKER_IMAGE_DATA_BYTES_PER_FRAME]);
|
|
|
|
/* Broadcast frames address every listening tag. */
|
|
size_t tagtinker_build_broadcast_page_frame(
|
|
uint8_t* buf, uint8_t page, bool forever, uint16_t duration);
|
|
|
|
size_t tagtinker_build_broadcast_debug_frame(uint8_t* buf);
|
|
|
|
/* Addressed frames use the PLID decoded from the barcode. */
|
|
size_t tagtinker_make_addressed_frame(
|
|
uint8_t* buf, const uint8_t plid[4],
|
|
const uint8_t* payload, size_t payload_len);
|
|
|
|
/* Tags need a wake ping before most addressed commands. */
|
|
size_t tagtinker_make_ping_frame(uint8_t* buf, const uint8_t plid[4]);
|
|
|
|
size_t tagtinker_make_wake_frame(uint8_t* buf, const uint8_t plid[4]);
|
|
|
|
size_t tagtinker_make_refresh_frame(uint8_t* buf, const uint8_t plid[4]);
|
|
|
|
/* Image upload uses MCU frames: one parameter frame followed by data frames. */
|
|
size_t tagtinker_make_mcu_frame(
|
|
uint8_t* buf, const uint8_t plid[4], uint8_t cmd);
|
|
|
|
/* RLE is the tag's compact bitmap format. Raw mode keeps one bit per pixel. */
|
|
size_t tagtinker_rle_compress(
|
|
const uint8_t* pixels, size_t count,
|
|
uint8_t* out, size_t out_cap, uint8_t* comp_type);
|
|
|
|
/* Builds the full IR sequence: wake, image params, data chunks, refresh. */
|
|
void tagtinker_build_image_sequence(
|
|
TagTinkerApp* app,
|
|
const uint8_t plid[4],
|
|
const uint8_t* pixels,
|
|
uint16_t width, uint16_t height,
|
|
uint8_t page,
|
|
uint16_t pos_x, uint16_t pos_y,
|
|
uint16_t wake_repeats);
|