2020-09-13 01:38:29 +08:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Waveshare commands
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// from ST25R3911B-NFC-Demo source code by Waveshare team
|
|
|
|
|
|
|
|
|
|
#include "cmdhfwaveshare.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include "comms.h"
|
|
|
|
|
#include "cmdparser.h"
|
|
|
|
|
#include "ui.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "fileutils.h"
|
|
|
|
|
#include "util_posix.h" // msleep
|
2020-10-06 17:44:55 +08:00
|
|
|
|
#include "cliparser.h"
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
|
|
|
|
// Currently the largest pixel 880*528 only needs 58.08K bytes
|
|
|
|
|
#define WSMAPSIZE 60000
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint8_t B;
|
|
|
|
|
uint8_t M;
|
|
|
|
|
uint32_t fsize;
|
|
|
|
|
uint16_t res1;
|
|
|
|
|
uint16_t res2;
|
|
|
|
|
uint32_t offset;
|
|
|
|
|
uint32_t Bit_Pixel;
|
|
|
|
|
uint32_t BMP_Width;
|
|
|
|
|
uint32_t BMP_Height;
|
|
|
|
|
uint16_t planes;
|
|
|
|
|
uint16_t bpp;
|
|
|
|
|
uint32_t ctype;
|
|
|
|
|
uint32_t dsize;
|
|
|
|
|
uint32_t hppm;
|
|
|
|
|
uint32_t vppm;
|
|
|
|
|
uint32_t colorsused;
|
|
|
|
|
uint32_t colorreq;
|
|
|
|
|
uint32_t Color_1; //Color palette
|
|
|
|
|
uint32_t Color_2;
|
|
|
|
|
} PACKED BMP_HEADER;
|
|
|
|
|
|
|
|
|
|
#define EPD_1IN54B 0
|
|
|
|
|
#define EPD_1IN54C 1
|
|
|
|
|
#define EPD_1IN54V2 2
|
|
|
|
|
#define EPD_1IN54BCV2 3
|
|
|
|
|
#define EPD_2IN13V2 4
|
|
|
|
|
#define EPD_2IN13BC 5
|
|
|
|
|
#define EPD_2IN13D 6
|
|
|
|
|
#define EPD_2IN9 7
|
|
|
|
|
#define EPD_2IN9BC 8
|
|
|
|
|
#define EPD_2IN9D 9
|
|
|
|
|
#define EPD_4IN2 10
|
|
|
|
|
#define EPD_4IN2BC 11
|
|
|
|
|
#define EPD_7IN5 12
|
|
|
|
|
#define EPD_7IN5BC 13
|
|
|
|
|
#define EPD_7IN5V2 14
|
|
|
|
|
#define EPD_7IN5BCV2 15
|
|
|
|
|
#define EPD_2IN7 16
|
|
|
|
|
#define EPD_7IN5HD 17
|
|
|
|
|
|
|
|
|
|
typedef struct model_s {
|
|
|
|
|
const char *desc;
|
|
|
|
|
uint8_t len; // The data sent in one time shall not be greater than 128-3
|
2020-09-14 07:16:28 +08:00
|
|
|
|
uint16_t width;
|
|
|
|
|
uint16_t height;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
} model_t;
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
M2in13 = 0,
|
|
|
|
|
M2in9,
|
|
|
|
|
M4in2,
|
|
|
|
|
M7in5,
|
|
|
|
|
M2in7,
|
|
|
|
|
M2in13B,
|
|
|
|
|
M1in54B,
|
|
|
|
|
M7in5HD,
|
|
|
|
|
MEND
|
|
|
|
|
} model_enum_t;
|
|
|
|
|
|
|
|
|
|
static model_t models[] = {
|
2020-09-16 05:59:53 +08:00
|
|
|
|
{"2.13 inch e-paper", 16, 122, 250}, // tested
|
|
|
|
|
{"2.9 inch e-paper", 16, 296, 128},
|
|
|
|
|
{"4.2 inch e-paper", 100, 400, 300}, // tested
|
|
|
|
|
{"7.5 inch e-paper", 120, 800, 480},
|
2020-11-13 04:35:36 +08:00
|
|
|
|
{"2.7 inch e-paper", 121, 176, 276}, // tested
|
2020-11-14 05:32:46 +08:00
|
|
|
|
{"2.13 inch e-paper B (with red)", 106, 104, 212}, // tested
|
2020-11-13 04:35:36 +08:00
|
|
|
|
{"1.54 inch e-paper B (with red)", 100, 200, 200}, // tested
|
2020-09-16 05:59:53 +08:00
|
|
|
|
{"7.5 inch e-paper HD", 120, 880, 528},
|
2020-09-13 01:38:29 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int CmdHelp(const char *Cmd);
|
|
|
|
|
|
2020-09-14 07:16:28 +08:00
|
|
|
|
static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
if (bmpsize < sizeof(BMP_HEADER))
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
|
|
|
|
|
PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused);
|
|
|
|
|
PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp);
|
2020-09-14 07:16:28 +08:00
|
|
|
|
if ((pbmpheader->BMP_Width != models[model_nr].width) || (pbmpheader->BMP_Height != models[model_nr].height)) {
|
2020-09-16 06:29:50 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Invalid BMP size, expected %ix%i, got %ix%i", models[model_nr].width, models[model_nr].height, pbmpheader->BMP_Width, pbmpheader->BMP_Height);
|
2020-09-14 07:16:28 +08:00
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
return pbmpheader->bpp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
static int read_bmp_bitmap(const uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
|
|
|
|
|
// check file is bitmap
|
|
|
|
|
if (pbmpheader->bpp != 1) {
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is not a BMP!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
|
|
|
|
|
PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
|
|
|
|
|
if (pbmpheader->fsize > bmpsize) {
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is truncated!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
uint8_t color_flag = pbmpheader->Color_1;
|
|
|
|
|
// Get BMP file data pointer
|
|
|
|
|
uint32_t offset = pbmpheader->offset;
|
2020-10-06 17:33:28 +08:00
|
|
|
|
uint16_t width = pbmpheader->BMP_Width;
|
|
|
|
|
uint16_t height = pbmpheader->BMP_Height;
|
|
|
|
|
if ((width + 8) * height > WSMAPSIZE * 8) {
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is too large, aborting!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
|
|
|
|
uint16_t X, Y;
|
2020-10-06 17:33:28 +08:00
|
|
|
|
uint16_t Image_Width_Byte = (width % 8 == 0) ? (width / 8) : (width / 8 + 1);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
uint16_t Bmp_Width_Byte = (Image_Width_Byte % 4 == 0) ? Image_Width_Byte : ((Image_Width_Byte / 4 + 1) * 4);
|
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
|
|
|
|
|
if (*black == NULL) {
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
// Write data into RAM
|
2020-10-06 17:33:28 +08:00
|
|
|
|
for (Y = 0; Y < height; Y++) { // columns
|
2020-09-16 15:19:41 +08:00
|
|
|
|
for (X = 0; X < Bmp_Width_Byte; X++) { // lines
|
2020-10-06 17:33:28 +08:00
|
|
|
|
if ((X < Image_Width_Byte) && ((X + (height - Y - 1) * Image_Width_Byte) < WSMAPSIZE)) {
|
|
|
|
|
(*black)[X + (height - Y - 1) * Image_Width_Byte] = color_flag ? bmp[offset] : ~bmp[offset];
|
2020-09-16 15:19:41 +08:00
|
|
|
|
}
|
|
|
|
|
offset++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-16 06:29:50 +08:00
|
|
|
|
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
|
|
|
|
|
// for BW+Red screens:
|
|
|
|
|
*red = calloc(WSMAPSIZE, sizeof(uint8_t));
|
|
|
|
|
if (*red == NULL) {
|
2020-09-16 15:19:41 +08:00
|
|
|
|
free(*black);
|
2020-09-16 06:29:50 +08:00
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-15 06:59:08 +08:00
|
|
|
|
static void rgb_to_gray(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, int16_t *chanGrey) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
// greyscale conversion
|
|
|
|
|
float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
|
|
|
|
|
// Csrgb = 12.92 Clinear when Clinear <= 0.0031308
|
|
|
|
|
// Csrgb = 1.055 Clinear1/2.4 - 0.055 when Clinear > 0.0031308
|
|
|
|
|
chanGrey[X + Y * width] = Clinear;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Floyd-Steinberg dithering
|
|
|
|
|
static void dither_chan_inplace(int16_t *chan, uint16_t width, uint16_t height) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
int16_t oldp = chan[X + Y * width];
|
|
|
|
|
int16_t newp = oldp > 127 ? 255 : 0;
|
|
|
|
|
chan[X + Y * width] = newp;
|
|
|
|
|
int16_t err = oldp - newp;
|
2020-09-30 20:27:19 +08:00
|
|
|
|
float m[] = {7, 3, 5, 1};
|
|
|
|
|
if (X < width - 1) {
|
|
|
|
|
chan[X + 1 + Y * width] = chan[X + 1 + Y * width] + m[0] / 16 * err;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if (Y < height - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chan[X - 1 + (Y + 1) * width] = chan[X - 1 + (Y + 1) * width] + m[1] / 16 * err;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
2020-09-30 20:27:19 +08:00
|
|
|
|
if (Y < height - 1) {
|
|
|
|
|
chan[X + (Y + 1) * width] = chan[X + (Y + 1) * width] + m[2] / 16 * err;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
2020-09-30 20:27:19 +08:00
|
|
|
|
if ((X < width - 1) && (Y < height - 1)) {
|
|
|
|
|
chan[X + 1 + (Y + 1) * width] = chan[X + 1 + (Y + 1) * width] + m[3] / 16 * err;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t color_compare(int16_t r1, int16_t g1, int16_t b1, int16_t r2, int16_t g2, int16_t b2) {
|
|
|
|
|
// Compute (square of) distance from oldR/G/B to this color
|
|
|
|
|
int16_t inR = r1 - r2;
|
|
|
|
|
int16_t inG = g1 - g2;
|
|
|
|
|
int16_t inB = b1 - b2;
|
|
|
|
|
// use RGB-to-grey weighting
|
2020-09-30 20:27:19 +08:00
|
|
|
|
float dist = 0.2126 * inR * inR + 0.7152 * inG * inG + 0.0722 * inB * inB;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
return dist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nearest_color(int16_t oldR, int16_t oldG, int16_t oldB, uint8_t *palette, uint16_t palettelen, uint8_t *newR, uint8_t *newG, uint8_t *newB) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
uint32_t bestdist = 0x7FFFFFFF;
|
|
|
|
|
for (uint16_t i = 0; i < palettelen; i++) {
|
|
|
|
|
uint8_t R = palette[i * 3 + 0];
|
|
|
|
|
uint8_t G = palette[i * 3 + 1];
|
|
|
|
|
uint8_t B = palette[i * 3 + 2];
|
2020-09-15 06:59:08 +08:00
|
|
|
|
uint32_t dist = color_compare(oldR, oldG, oldB, R, G, B);
|
|
|
|
|
if (dist < bestdist) {
|
|
|
|
|
bestdist = dist;
|
|
|
|
|
*newR = R;
|
|
|
|
|
*newG = G;
|
|
|
|
|
*newB = B;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dither_rgb_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t *palette, uint16_t palettelen) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
// scan odd lines in the opposite direction
|
|
|
|
|
uint16_t XX = X;
|
|
|
|
|
if (Y % 2) {
|
|
|
|
|
XX = width - X - 1;
|
|
|
|
|
}
|
|
|
|
|
int16_t oldR = chanR[XX + Y * width];
|
|
|
|
|
int16_t oldG = chanG[XX + Y * width];
|
|
|
|
|
int16_t oldB = chanB[XX + Y * width];
|
2020-10-06 20:32:14 +08:00
|
|
|
|
uint8_t newR = 0, newG = 0, newB = 0;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
nearest_color(oldR, oldG, oldB, palette, palettelen, &newR, &newG, &newB);
|
|
|
|
|
chanR[XX + Y * width] = newR;
|
|
|
|
|
chanG[XX + Y * width] = newG;
|
|
|
|
|
chanB[XX + Y * width] = newB;
|
|
|
|
|
int16_t errR = oldR - newR;
|
|
|
|
|
int16_t errG = oldG - newG;
|
|
|
|
|
int16_t errB = oldB - newB;
|
2020-09-30 20:27:19 +08:00
|
|
|
|
float m[] = {7, 3, 5, 1};
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if (Y % 2) {
|
|
|
|
|
if (XX > 0) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX - 1 + Y * width] = (chanR[XX - 1 + Y * width] + m[0] / 16 * errR);
|
|
|
|
|
chanG[XX - 1 + Y * width] = (chanG[XX - 1 + Y * width] + m[0] / 16 * errG);
|
|
|
|
|
chanB[XX - 1 + Y * width] = (chanB[XX - 1 + Y * width] + m[0] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if (Y < height - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[3] / 16 * errR);
|
|
|
|
|
chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[3] / 16 * errG);
|
|
|
|
|
chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[3] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if (Y < height - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
|
|
|
|
|
chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
|
|
|
|
|
chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if ((XX < width - 1) && (Y < height - 1)) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[1] / 16 * errR);
|
|
|
|
|
chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[1] / 16 * errG);
|
|
|
|
|
chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[1] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (XX < width - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX + 1 + Y * width] = (chanR[XX + 1 + Y * width] + m[0] / 16 * errR);
|
|
|
|
|
chanG[XX + 1 + Y * width] = (chanG[XX + 1 + Y * width] + m[0] / 16 * errG);
|
|
|
|
|
chanB[XX + 1 + Y * width] = (chanB[XX + 1 + Y * width] + m[0] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if (Y < height - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[1] / 16 * errR);
|
|
|
|
|
chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[1] / 16 * errG);
|
|
|
|
|
chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[1] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if (Y < height - 1) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
|
|
|
|
|
chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
|
|
|
|
|
chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
if ((XX < width - 1) && (Y < height - 1)) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[3] / 16 * errR);
|
|
|
|
|
chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[3] / 16 * errG);
|
|
|
|
|
chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[3] / 16 * errB);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rgb_to_gray_red_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
|
|
|
|
|
if ((chanR[X + Y * width] < chanG[X + Y * width] && chanR[X + Y * width] < chanB[X + Y * width])) {
|
|
|
|
|
chanR[X + Y * width] = Clinear;
|
|
|
|
|
chanG[X + Y * width] = Clinear;
|
|
|
|
|
chanB[X + Y * width] = Clinear;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void threshold_chan(int16_t *colorchan, uint16_t width, uint16_t height, uint8_t threshold, uint8_t *colormap) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
colormap[X + Y * width] = colorchan[X + Y * width] < threshold;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void threshold_rgb_black_red(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t threshold_black, uint8_t threshold_red, uint8_t *blackmap, uint8_t *redmap) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if ((chanR[X + Y * width] < threshold_black) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
|
|
|
|
|
blackmap[X + Y * width] = 1;
|
|
|
|
|
redmap[X + Y * width] = 0;
|
|
|
|
|
} else if ((chanR[X + Y * width] > threshold_red) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
|
|
|
|
|
blackmap[X + Y * width] = 0;
|
|
|
|
|
redmap[X + Y * width] = 1;
|
|
|
|
|
} else {
|
|
|
|
|
blackmap[X + Y * width] = 0;
|
|
|
|
|
redmap[X + Y * width] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void map8to1(uint8_t *colormap, uint16_t width, uint16_t height, uint8_t *colormap8) {
|
|
|
|
|
uint16_t width8;
|
|
|
|
|
if (width % 8 == 0) {
|
|
|
|
|
width8 = width / 8;
|
|
|
|
|
} else {
|
|
|
|
|
width8 = width / 8 + 1;
|
|
|
|
|
}
|
|
|
|
|
uint8_t data = 0;
|
|
|
|
|
uint8_t count = 0;
|
2020-09-30 20:27:19 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
data = data | colormap[X + Y * width];
|
2020-09-30 20:27:19 +08:00
|
|
|
|
count += 1;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if ((count >= 8) || (X == width - 1)) {
|
2020-09-30 20:27:19 +08:00
|
|
|
|
colormap8[X / 8 + Y * width8] = (~data) & 0xFF;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
count = 0;
|
|
|
|
|
data = 0;
|
|
|
|
|
}
|
2020-09-30 20:27:19 +08:00
|
|
|
|
data = (data << 1) & 0xFF;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red, char *filename, bool save_conversions) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
|
|
|
|
|
// check file is full color
|
2020-10-20 03:25:24 +08:00
|
|
|
|
if ((pbmpheader->bpp != 24) && (pbmpheader->bpp != 32)) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
2020-09-13 01:38:29 +08:00
|
|
|
|
if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is not a BMP!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
|
|
|
|
|
PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
|
|
|
|
|
if (pbmpheader->fsize > bmpsize) {
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is truncated!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
2020-09-13 01:38:29 +08:00
|
|
|
|
// Get BMP file data pointer
|
|
|
|
|
uint32_t offset = pbmpheader->offset;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
uint16_t width = pbmpheader->BMP_Width;
|
|
|
|
|
uint16_t height = pbmpheader->BMP_Height;
|
2020-10-06 17:33:28 +08:00
|
|
|
|
if ((width + 8) * height > WSMAPSIZE * 8) {
|
|
|
|
|
PrintAndLogEx(WARNING, "The file is too large, aborting!");
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
2020-09-18 05:08:49 +08:00
|
|
|
|
int16_t *chanR = calloc(width * height, sizeof(int16_t));
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if (chanR == NULL) {
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
|
|
|
|
int16_t *chanG = calloc(width * height, sizeof(int16_t));
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if (chanG == NULL) {
|
|
|
|
|
free(chanR);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
|
|
|
|
int16_t *chanB = calloc(width * height, sizeof(int16_t));
|
2020-09-15 06:59:08 +08:00
|
|
|
|
if (chanB == NULL) {
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extracting BMP chans
|
2020-09-18 05:08:49 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
chanB[X + (height - Y - 1) * width] = bmp[offset++];
|
|
|
|
|
chanG[X + (height - Y - 1) * width] = bmp[offset++];
|
|
|
|
|
chanR[X + (height - Y - 1) * width] = bmp[offset++];
|
2020-10-20 03:25:24 +08:00
|
|
|
|
if (pbmpheader->bpp == 32) // Skip Alpha chan
|
|
|
|
|
offset++;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
// Skip line padding
|
2020-09-18 05:08:49 +08:00
|
|
|
|
offset += width % 4;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
|
|
|
|
|
// for BW+Red screens:
|
2020-09-18 05:08:49 +08:00
|
|
|
|
uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t));
|
2020-09-16 15:19:41 +08:00
|
|
|
|
if (mapBlack == NULL) {
|
2020-09-16 05:59:53 +08:00
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
uint8_t *mapRed = calloc(width * height, sizeof(uint8_t));
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if (mapRed == NULL) {
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
2020-09-16 15:19:41 +08:00
|
|
|
|
free(mapBlack);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
rgb_to_gray_red_inplace(chanR, chanG, chanB, width, height);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
|
uint8_t palette[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; // black, white, red
|
|
|
|
|
dither_rgb_inplace(chanR, chanG, chanB, width, height, palette, sizeof(palette) / 3);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
threshold_rgb_black_red(chanR, chanG, chanB, width, height, 128, 128, mapBlack, mapRed);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if (save_conversions) {
|
|
|
|
|
// fill BMP chans
|
|
|
|
|
offset = pbmpheader->offset;
|
2020-09-18 05:08:49 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-16 05:59:53 +08:00
|
|
|
|
bmp[offset++] = chanB[X + (height - Y - 1) * width] & 0xFF;
|
|
|
|
|
bmp[offset++] = chanG[X + (height - Y - 1) * width] & 0xFF;
|
|
|
|
|
bmp[offset++] = chanR[X + (height - Y - 1) * width] & 0xFF;
|
2020-10-20 03:25:24 +08:00
|
|
|
|
if (pbmpheader->bpp == 32) // Fill Alpha chan
|
|
|
|
|
bmp[offset++] = 0xFF;
|
2020-09-16 05:59:53 +08:00
|
|
|
|
}
|
|
|
|
|
// Skip line padding
|
2020-09-18 05:08:49 +08:00
|
|
|
|
offset += width % 4;
|
2020-09-16 05:59:53 +08:00
|
|
|
|
}
|
|
|
|
|
PrintAndLogEx(INFO, "Saving red+black dithered version...");
|
|
|
|
|
if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
2020-09-16 15:19:41 +08:00
|
|
|
|
free(mapBlack);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
free(mapRed);
|
|
|
|
|
return PM3_EIO;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
2020-09-16 15:19:41 +08:00
|
|
|
|
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
|
|
|
|
|
if (*black == NULL) {
|
|
|
|
|
free(mapBlack);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
free(mapRed);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
2020-09-16 15:19:41 +08:00
|
|
|
|
map8to1(mapBlack, width, height, *black);
|
|
|
|
|
free(mapBlack);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
*red = calloc(WSMAPSIZE, sizeof(uint8_t));
|
|
|
|
|
if (*red == NULL) {
|
|
|
|
|
free(mapRed);
|
2020-09-16 15:19:41 +08:00
|
|
|
|
free(*black);
|
2020-09-16 05:59:53 +08:00
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
map8to1(mapRed, width, height, *red);
|
|
|
|
|
free(mapRed);
|
|
|
|
|
} else {
|
|
|
|
|
// for BW-only screens:
|
2020-09-18 05:08:49 +08:00
|
|
|
|
int16_t *chanGrey = calloc(width * height, sizeof(int16_t));
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if (chanGrey == NULL) {
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
rgb_to_gray(chanR, chanG, chanB, width, height, chanGrey);
|
|
|
|
|
dither_chan_inplace(chanGrey, width, height);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
|
2020-09-18 05:08:49 +08:00
|
|
|
|
uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t));
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if (mapBlack == NULL) {
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
|
|
|
|
free(chanGrey);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
threshold_chan(chanGrey, width, height, 128, mapBlack);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
2020-09-16 05:59:53 +08:00
|
|
|
|
if (save_conversions) {
|
|
|
|
|
// fill BMP chans
|
|
|
|
|
offset = pbmpheader->offset;
|
2020-09-18 05:08:49 +08:00
|
|
|
|
for (uint16_t Y = 0; Y < height; Y++) {
|
|
|
|
|
for (uint16_t X = 0; X < width; X++) {
|
2020-09-16 05:59:53 +08:00
|
|
|
|
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
|
|
|
|
|
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
|
|
|
|
|
bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
|
2020-10-20 03:25:24 +08:00
|
|
|
|
if (pbmpheader->bpp == 32) // Fill Alpha chan
|
|
|
|
|
bmp[offset++] = 0xFF;
|
2020-09-16 05:59:53 +08:00
|
|
|
|
}
|
|
|
|
|
// Skip line padding
|
2020-09-18 05:08:49 +08:00
|
|
|
|
offset += width % 4;
|
2020-09-16 05:59:53 +08:00
|
|
|
|
}
|
|
|
|
|
PrintAndLogEx(INFO, "Saving black dithered version...");
|
|
|
|
|
if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
|
|
|
|
|
free(chanGrey);
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
|
|
|
|
free(mapBlack);
|
|
|
|
|
return PM3_EIO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(chanGrey);
|
|
|
|
|
free(chanR);
|
|
|
|
|
free(chanG);
|
|
|
|
|
free(chanB);
|
|
|
|
|
*black = calloc(WSMAPSIZE, sizeof(uint8_t));
|
|
|
|
|
if (*black == NULL) {
|
|
|
|
|
free(mapBlack);
|
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
|
}
|
|
|
|
|
map8to1(mapBlack, width, height, *black);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
free(mapBlack);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *black) {
|
|
|
|
|
for (uint8_t j = 0; j < models[model_nr].len; j++) {
|
|
|
|
|
l[3 + j] = black[i * models[model_nr].len + j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void read_red(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *red) {
|
2020-10-21 01:28:57 +08:00
|
|
|
|
// spurious warning with GCC10 (-Wstringop-overflow) when j is uint8_t, even if all len are < 128
|
|
|
|
|
for (uint16_t j = 0; j < models[model_nr].len; j++) {
|
2020-09-16 06:29:50 +08:00
|
|
|
|
if (model_nr == M1in54B) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
//1.54B needs to flip the red picture data, other screens do not need to flip data
|
|
|
|
|
l[3 + j] = ~red[i * models[model_nr].len + j];
|
|
|
|
|
} else {
|
|
|
|
|
l[3 + j] = red[i * models[model_nr].len + j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
|
static int transceive_blocking(uint8_t *txBuf, uint16_t txBufLen, uint8_t *rxBuf, uint16_t rxBufLen, uint16_t *actLen, bool retransmit) {
|
2020-09-13 09:10:33 +08:00
|
|
|
|
uint8_t fail_num = 0;
|
2020-09-18 05:08:49 +08:00
|
|
|
|
if (rxBufLen < 2) {
|
2020-09-13 09:10:33 +08:00
|
|
|
|
return PM3_EINVARG;
|
2020-09-18 05:08:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 09:10:33 +08:00
|
|
|
|
while (1) {
|
|
|
|
|
PacketResponseNG resp;
|
|
|
|
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, txBufLen, 0, txBuf, txBufLen);
|
|
|
|
|
rxBuf[0] = 1;
|
|
|
|
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
|
|
|
|
|
if (resp.oldarg[0] > rxBufLen) {
|
2020-10-05 19:56:12 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Received %"PRIu64 " bytes, rxBuf too small (%u)", resp.oldarg[0], rxBufLen);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
memcpy(rxBuf, resp.data.asBytes, rxBufLen);
|
|
|
|
|
*actLen = rxBufLen;
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
memcpy(rxBuf, resp.data.asBytes, resp.oldarg[0]);
|
|
|
|
|
*actLen = resp.oldarg[0];
|
|
|
|
|
}
|
2020-09-18 05:08:49 +08:00
|
|
|
|
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if ((retransmit) && (rxBuf[0] != 0 || rxBuf[1] != 0)) {
|
|
|
|
|
fail_num++;
|
|
|
|
|
if (fail_num > 10) {
|
|
|
|
|
PROMPT_CLEARLINE;
|
|
|
|
|
PrintAndLogEx(WARNING, "Transmission failed, please try again.");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return PM3_SUCCESS;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1.54B Keychain
|
|
|
|
|
// 1.54B does not share the common base and requires specific handling
|
2020-09-13 09:10:33 +08:00
|
|
|
|
static int start_drawing_1in54B(uint8_t model_nr, uint8_t *black, uint8_t *red) {
|
|
|
|
|
int ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
uint8_t step_5[128] = {0xcd, 0x05, 100};
|
|
|
|
|
uint8_t step_4[2] = {0xcd, 0x04};
|
|
|
|
|
uint8_t step_6[2] = {0xcd, 0x06};
|
|
|
|
|
uint8_t rx[20] = {0};
|
|
|
|
|
uint16_t actrxlen[20], i = 0, progress = 0;
|
|
|
|
|
|
|
|
|
|
if (model_nr == M1in54B) {
|
|
|
|
|
step_5[2] = 100;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "1.54_Step9: e-paper config2 (black)");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M1in54B) { //1.54inch B Keychain
|
|
|
|
|
for (i = 0; i < 50; i++) {
|
|
|
|
|
read_black(i, step_5, model_nr, black);
|
|
|
|
|
ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 100;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PROMPT_CLEARLINE;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "1.54_Step6: e-paper power on");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step_4, 2, rx, 20, actrxlen, true); //cd 04
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "1.54_Step7: e-paper config2 (red)");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M1in54B) { //1.54inch B Keychain
|
|
|
|
|
for (i = 0; i < 50; i++) {
|
|
|
|
|
read_red(i, step_5, model_nr, red);
|
|
|
|
|
ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 100 + 50;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
PROMPT_CLEARLINE;
|
|
|
|
|
// Send update instructions
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "1.54_Step8: EDP load to main");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step_6, 2, rx, 20, actrxlen, true); //cd 06
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "1.54_Step9");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
return PM3_SUCCESS;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) {
|
2020-09-13 09:10:33 +08:00
|
|
|
|
uint8_t progress = 0;
|
|
|
|
|
uint8_t step0[2] = {0xcd, 0x0d};
|
2020-10-11 04:52:15 +08:00
|
|
|
|
uint8_t step1[3] = {0xcd, 0x00, 10}; // select e-paper type and reset e-paper
|
2020-10-12 04:40:05 +08:00
|
|
|
|
// 4 :2.13inch e-Paper
|
|
|
|
|
// 7 :2.9inch e-Paper
|
|
|
|
|
// 10 :4.2inch e-Paper
|
|
|
|
|
// 14 :7.5inch e-Paper
|
2020-10-11 04:52:15 +08:00
|
|
|
|
uint8_t step2[2] = {0xcd, 0x01}; // e-paper normal mode type:
|
|
|
|
|
uint8_t step3[2] = {0xcd, 0x02}; // e-paper config1
|
|
|
|
|
uint8_t step4[2] = {0xcd, 0x03}; // e-paper power on
|
|
|
|
|
uint8_t step5[2] = {0xcd, 0x05}; // e-paper config2
|
|
|
|
|
uint8_t step6[2] = {0xcd, 0x06}; // EDP load to main
|
|
|
|
|
uint8_t step7[2] = {0xcd, 0x07}; // Data preparation
|
|
|
|
|
|
2020-10-12 04:40:05 +08:00
|
|
|
|
uint8_t step8[123] = {0xcd, 0x08, 0x64}; // Data start command
|
|
|
|
|
// 2.13inch(0x10:Send 16 data at a time)
|
|
|
|
|
// 2.9inch(0x10:Send 16 data at a time)
|
|
|
|
|
// 4.2inch(0x64:Send 100 data at a time)
|
|
|
|
|
// 7.5inch(0x78:Send 120 data at a time)
|
2020-10-11 04:52:15 +08:00
|
|
|
|
uint8_t step9[2] = {0xcd, 0x18}; // e-paper power on
|
|
|
|
|
uint8_t step10[2] = {0xcd, 0x09}; // Refresh e-paper
|
|
|
|
|
uint8_t step11[2] = {0xcd, 0x0a}; // wait for ready
|
|
|
|
|
uint8_t step12[2] = {0xcd, 0x04}; // e-paper power off command
|
2020-09-13 09:10:33 +08:00
|
|
|
|
uint8_t step13[124] = {0xcd, 0x19, 121};
|
2020-10-11 04:52:15 +08:00
|
|
|
|
// uint8_t step13[2]={0xcd,0x0b}; // Judge whether the power supply is turned off successfully
|
|
|
|
|
// uint8_t step14[2]={0xcd,0x0c}; // The end of the transmission
|
2020-09-13 09:10:33 +08:00
|
|
|
|
uint8_t rx[20];
|
|
|
|
|
uint16_t actrxlen[20], i = 0;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clearCommandBuffer();
|
|
|
|
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
|
|
|
|
|
PacketResponseNG resp;
|
|
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
|
|
|
|
|
PrintAndLogEx(ERR, "No tag found");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ETIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iso14a_card_select_t card;
|
|
|
|
|
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
|
|
|
|
|
|
|
|
|
|
uint64_t select_status = resp.oldarg[0];
|
|
|
|
|
|
|
|
|
|
if (select_status == 0) {
|
|
|
|
|
PrintAndLogEx(ERR, "Tag select error");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ERFTRANS;
|
|
|
|
|
} else if (select_status == 3) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Card doesn't support standard iso14443-3 anticollision, doesn't look like Waveshare tag");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-19 21:48:45 +08:00
|
|
|
|
if ((card.uidlen != 7) || ((memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "WSDZ10m", 7) != 0))) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Card doesn't look like Waveshare tag");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-10-19 21:48:45 +08:00
|
|
|
|
if (((model_nr != M1in54B) && (memcmp(card.uid, "FSTN10m", 7) == 0))) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Card is a Waveshare tag 1.54\", not %s", models[model_nr].desc);
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
if (((model_nr == M1in54B) && (memcmp(card.uid, "FSTN10m", 7) != 0))) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Card is not a Waveshare tag 1.54\", check your model number");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "model_nr = %d", model_nr);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
int ret;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step0");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step0, 2, rx, 20, actrxlen, true); //cd 0d
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step1: e-paper config");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
//step1[2] screen model
|
|
|
|
|
//step8[2] nr of bytes sent at once
|
|
|
|
|
//step13[2] nr of bytes sent for the second time
|
|
|
|
|
// generally, step8 sends a black image, step13 sends a red image
|
|
|
|
|
if (model_nr == M2in13) { //2.13inch
|
|
|
|
|
step1[2] = EPD_2IN13V2;
|
|
|
|
|
step8[2] = 16;
|
|
|
|
|
step13[2] = 0;
|
|
|
|
|
} else if (model_nr == M2in9) { //2.9inch
|
|
|
|
|
step1[2] = EPD_2IN9;
|
|
|
|
|
step8[2] = 16;
|
|
|
|
|
step13[2] = 0;
|
|
|
|
|
} else if (model_nr == M4in2) { //4.2inch
|
|
|
|
|
step1[2] = EPD_4IN2;
|
|
|
|
|
step8[2] = 100;
|
|
|
|
|
step13[2] = 0;
|
|
|
|
|
} else if (model_nr == M7in5) { //7.5inch
|
|
|
|
|
step1[2] = EPD_7IN5V2;
|
|
|
|
|
step8[2] = 120;
|
|
|
|
|
step13[2] = 0;
|
|
|
|
|
} else if (model_nr == M2in7) { //2.7inch
|
|
|
|
|
step1[2] = EPD_2IN7;
|
|
|
|
|
step8[2] = 121;
|
|
|
|
|
// Send blank data for the first time, and send other data to 0xff without processing the bottom layer
|
|
|
|
|
step13[2] = 121;
|
|
|
|
|
//Sending the second data is the real image data. If the previous 0xff is not sent, the last output image is abnormally black
|
|
|
|
|
} else if (model_nr == M2in13B) { //2.13inch B
|
|
|
|
|
step1[2] = EPD_2IN13BC;
|
|
|
|
|
step8[2] = 106;
|
|
|
|
|
step13[2] = 106;
|
|
|
|
|
} else if (model_nr == M7in5HD) {
|
|
|
|
|
step1[2] = EPD_7IN5HD;
|
|
|
|
|
step8[2] = 120;
|
|
|
|
|
step13[2] = 0;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M1in54B) {
|
|
|
|
|
ret = transceive_blocking(step1, 2, rx, 20, actrxlen, true); //cd 00
|
|
|
|
|
} else {
|
|
|
|
|
ret = transceive_blocking(step1, 3, rx, 20, actrxlen, true);
|
|
|
|
|
}
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(100);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step2: e-paper normal mode type");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step2, 2, rx, 20, actrxlen, true); //cd 01
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(100);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step3: e-paper config1");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step3, 2, rx, 20, actrxlen, true); //cd 02
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(200);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step4: e-paper power on");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step4, 2, rx, 20, actrxlen, true); //cd 03
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
if (model_nr == M1in54B) {
|
|
|
|
|
// 1.54B Keychain handler
|
|
|
|
|
PrintAndLogEx(DEBUG, "Start_Drawing_1in54B");
|
2020-09-16 15:19:41 +08:00
|
|
|
|
ret = start_drawing_1in54B(model_nr, black, red);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
//1.54B Data transfer is complete and wait for refresh
|
|
|
|
|
} else {
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step5: e-paper config2");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step5, 2, rx, 20, actrxlen, true); //cd 05
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(100);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step6: EDP load to main") ;
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step6, 2, rx, 20, actrxlen, true); //cd 06
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(100);
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step7: Data preparation");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step7, 2, rx, 20, actrxlen, true); //cd 07
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step8: Start data transfer");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M2in13) { //2.13inch
|
|
|
|
|
for (i = 0; i < 250; i++) {
|
|
|
|
|
read_black(i, step8, model_nr, black);
|
|
|
|
|
ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 250;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M2in9) {
|
|
|
|
|
for (i = 0; i < 296; i++) {
|
|
|
|
|
read_black(i, step8, model_nr, black);
|
|
|
|
|
ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 296;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M4in2) { //4.2inch
|
|
|
|
|
for (i = 0; i < 150; i++) {
|
|
|
|
|
read_black(i, step8, model_nr, black);
|
|
|
|
|
ret = transceive_blocking(step8, 103, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 150;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M7in5) { //7.5inch
|
|
|
|
|
for (i = 0; i < 400; i++) {
|
|
|
|
|
read_black(i, step8, model_nr, black);
|
|
|
|
|
ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 400;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
|
|
|
|
msleep(6);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M2in13B) { //2.13inch B
|
|
|
|
|
for (i = 0; i < 26; i++) {
|
2020-09-16 15:19:41 +08:00
|
|
|
|
read_black(i, step8, model_nr, black);
|
2020-11-14 05:32:46 +08:00
|
|
|
|
ret = transceive_blocking(step8, 109, rx, 20, actrxlen, false); // cd 08
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 50 / 26;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M7in5HD) { //7.5HD
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 484; i++) {
|
|
|
|
|
read_black(i, step8, model_nr, black);
|
|
|
|
|
//memset(&step8[3], 0xf0, 120);
|
|
|
|
|
ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 100 / 484;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
memset(&step8[3], 0xff, 120);
|
|
|
|
|
ret = transceive_blocking(step8, 110 + 3, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M2in7) { //2.7inch
|
|
|
|
|
for (i = 0; i < 48; i++) {
|
|
|
|
|
//read_black(i,step8, model_nr, black);
|
2020-09-30 20:27:19 +08:00
|
|
|
|
memset(&step8[3], 0xFF, sizeof(step8) - 3);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step8, 124, rx, 20, actrxlen, true); // cd 08
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
progress = i * 50 / 48;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
}
|
|
|
|
|
PROMPT_CLEARLINE;
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step9: e-paper power on");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M2in13 || model_nr == M2in9 || model_nr == M4in2 || model_nr == M7in5 || model_nr == M7in5HD) {
|
|
|
|
|
ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
|
|
|
|
|
// The black-and-white screen sending backplane is also shielded, with no effect. Except 2.7
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-13 09:10:33 +08:00
|
|
|
|
} else if (model_nr == M2in13B || model_nr == M2in7) {
|
|
|
|
|
ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step9b");
|
2020-09-13 01:38:29 +08:00
|
|
|
|
if (model_nr == M2in7) {
|
|
|
|
|
for (i = 0; i < 48; i++) {
|
|
|
|
|
read_black(i, step13, model_nr, black);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step13, 124, rx, 20, actrxlen, true); //CD 19
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
progress = i * 50 / 48 + 50;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
|
|
|
|
}
|
|
|
|
|
} else if (model_nr == M2in13B) {
|
|
|
|
|
for (i = 0; i < 26; i++) {
|
|
|
|
|
read_red(i, step13, model_nr, red);
|
|
|
|
|
//memset(&step13[3], 0xfE, 106);
|
2020-11-14 05:32:46 +08:00
|
|
|
|
ret = transceive_blocking(step13, 109, rx, 20, actrxlen, false);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
progress = i * 50 / 26 + 50;
|
|
|
|
|
PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PROMPT_CLEARLINE;
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step10: Refresh e-paper");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step10, 2, rx, 20, actrxlen, true); //cd 09 refresh command
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(200);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step11: Wait tag to be ready");
|
2020-10-19 21:48:45 +08:00
|
|
|
|
PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
if (model_nr == M2in13B || model_nr == M1in54B) { // Black, white and red screen refresh time is longer, wait first
|
|
|
|
|
msleep(9000);
|
|
|
|
|
} else if (model_nr == M7in5HD) {
|
|
|
|
|
msleep(1000);
|
|
|
|
|
}
|
|
|
|
|
uint8_t fail_num = 0;
|
|
|
|
|
while (1) {
|
|
|
|
|
if (model_nr == M1in54B) {
|
|
|
|
|
// send 0xcd 0x08 with 1.54B
|
|
|
|
|
ret = transceive_blocking(step8, 2, rx, 20, actrxlen, false); //cd 08
|
|
|
|
|
} else {
|
|
|
|
|
ret = transceive_blocking(step11, 2, rx, 20, actrxlen, false); //cd 0a
|
|
|
|
|
}
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
if (rx[0] == 0xff && rx[1] == 0) {
|
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
|
PrintAndLogEx(SUCCESS, "E-paper Reflash OK");
|
|
|
|
|
msleep(200);
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
if (fail_num > 50) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Update failed, please try again.");
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
} else {
|
|
|
|
|
fail_num++;
|
|
|
|
|
PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
|
2020-10-19 21:48:45 +08:00
|
|
|
|
msleep(400);
|
2020-09-13 09:10:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-15 06:59:08 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "Step12: e-paper power off command");
|
2020-09-13 09:10:33 +08:00
|
|
|
|
ret = transceive_blocking(step12, 2, rx, 20, actrxlen, true); //cd 04
|
|
|
|
|
if (ret != PM3_SUCCESS) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
msleep(200);
|
|
|
|
|
PrintAndLogEx(SUCCESS, "E-paper Update OK");
|
|
|
|
|
msleep(200);
|
|
|
|
|
DropField();
|
|
|
|
|
return PM3_SUCCESS;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int CmdHF14AWSLoadBmp(const char *Cmd) {
|
|
|
|
|
|
2020-10-06 17:44:55 +08:00
|
|
|
|
char desc[800] = {0};
|
|
|
|
|
for (uint8_t i = 0; i < MEND; i++) {
|
|
|
|
|
snprintf(desc + strlen(desc),
|
2020-10-07 00:00:00 +08:00
|
|
|
|
sizeof(desc) - strlen(desc),
|
|
|
|
|
"hf waveshare loadbmp -f myfile -m %2u -> %s ( %u, %u )\n",
|
|
|
|
|
i,
|
|
|
|
|
models[i].desc,
|
|
|
|
|
models[i].width,
|
|
|
|
|
models[i].height
|
|
|
|
|
);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 17:44:55 +08:00
|
|
|
|
CLIParserContext *ctx;
|
|
|
|
|
CLIParserInit(&ctx, "hf waveshare loadbmp",
|
|
|
|
|
"Load BMP file to Waveshare NFC ePaper.",
|
|
|
|
|
desc
|
2020-10-07 00:00:00 +08:00
|
|
|
|
);
|
|
|
|
|
|
2020-10-06 17:44:55 +08:00
|
|
|
|
char modeldesc[40];
|
|
|
|
|
snprintf(modeldesc, sizeof(modeldesc), "model number [0 - %u] of your tag", MEND - 1);
|
2020-10-07 00:00:00 +08:00
|
|
|
|
|
2020-10-06 17:44:55 +08:00
|
|
|
|
void *argtable[] = {
|
|
|
|
|
arg_param_begin,
|
|
|
|
|
arg_int1("m", NULL, "<nr>", modeldesc),
|
|
|
|
|
arg_lit0("s", "save", "save dithered version in filename-[n].bmp, only for RGB BMP"),
|
2020-10-06 17:58:32 +08:00
|
|
|
|
arg_str1("f", "file", "<filename>", "filename[.bmp] to upload to tag"),
|
2020-10-06 17:44:55 +08:00
|
|
|
|
arg_param_end
|
2020-10-07 00:00:00 +08:00
|
|
|
|
};
|
2020-10-06 17:44:55 +08:00
|
|
|
|
|
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
|
|
|
|
|
|
int model_nr = arg_get_int_def(ctx, 1, -1);
|
|
|
|
|
bool save_conversions = arg_get_lit(ctx, 2);
|
|
|
|
|
|
|
|
|
|
int fnlen = 0;
|
|
|
|
|
char filename[FILE_PATH_SIZE] = {0};
|
2020-10-07 00:00:00 +08:00
|
|
|
|
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
2020-10-06 17:44:55 +08:00
|
|
|
|
CLIParserFree(ctx);
|
|
|
|
|
|
2020-09-13 01:38:29 +08:00
|
|
|
|
//Validations
|
2020-10-06 17:44:55 +08:00
|
|
|
|
if (fnlen < 1) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Missing filename");
|
2020-10-06 17:44:55 +08:00
|
|
|
|
return PM3_EINVARG;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-10-06 17:44:55 +08:00
|
|
|
|
if (model_nr == -1) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Missing model");
|
2020-10-06 17:44:55 +08:00
|
|
|
|
return PM3_EINVARG;
|
2020-10-07 00:00:00 +08:00
|
|
|
|
}
|
2020-10-06 17:44:55 +08:00
|
|
|
|
if (model_nr >= MEND) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
PrintAndLogEx(WARNING, "Unknown model");
|
2020-10-06 17:44:55 +08:00
|
|
|
|
return PM3_EINVARG;
|
2020-09-13 01:38:29 +08:00
|
|
|
|
}
|
2020-10-06 17:44:55 +08:00
|
|
|
|
|
2020-09-13 01:38:29 +08:00
|
|
|
|
uint8_t *bmp = NULL;
|
|
|
|
|
uint8_t *black = NULL;
|
|
|
|
|
uint8_t *red = NULL;
|
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
|
if (loadFile_safe(filename, ".bmp", (void **)&bmp, &bytes_read) != PM3_SUCCESS) {
|
|
|
|
|
PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename);
|
|
|
|
|
return PM3_EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 07:16:28 +08:00
|
|
|
|
int depth = picture_bit_depth(bmp, bytes_read, model_nr);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
if (depth == PM3_ESOFT) {
|
|
|
|
|
PrintAndLogEx(ERR, "Error, BMP file is too small");
|
|
|
|
|
free(bmp);
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
} else if (depth == 1) {
|
|
|
|
|
PrintAndLogEx(DEBUG, "BMP file is a bitmap");
|
2020-09-16 15:19:41 +08:00
|
|
|
|
if (read_bmp_bitmap(bmp, bytes_read, model_nr, &black, &red) != PM3_SUCCESS) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
free(bmp);
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
} else if (depth == 24) {
|
|
|
|
|
PrintAndLogEx(DEBUG, "BMP file is a RGB");
|
2020-09-16 15:19:41 +08:00
|
|
|
|
if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
free(bmp);
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
} else if (depth == 32) {
|
2020-10-20 03:25:24 +08:00
|
|
|
|
PrintAndLogEx(DEBUG, "BMP file is a RGBA, we will ignore the Alpha channel");
|
|
|
|
|
if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
|
|
|
|
|
free(bmp);
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
2020-09-13 01:38:29 +08:00
|
|
|
|
} else {
|
2020-10-20 03:25:24 +08:00
|
|
|
|
PrintAndLogEx(ERR, "Error, BMP color depth %i not supported. Must be 1 (BW), 24 (RGB) or 32 (RGBA)", depth);
|
2020-09-13 01:38:29 +08:00
|
|
|
|
free(bmp);
|
|
|
|
|
return PM3_ESOFT;
|
|
|
|
|
}
|
|
|
|
|
free(bmp);
|
|
|
|
|
|
2020-09-16 15:19:41 +08:00
|
|
|
|
start_drawing(model_nr, black, red);
|
|
|
|
|
free(black);
|
|
|
|
|
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
|
2020-09-13 01:38:29 +08:00
|
|
|
|
free(red);
|
|
|
|
|
}
|
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static command_t CommandTable[] = {
|
|
|
|
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
|
|
|
|
{"loadbmp", CmdHF14AWSLoadBmp, IfPm3Iso14443a, "Load BMP file to Waveshare NFC ePaper"},
|
|
|
|
|
{NULL, NULL, NULL, NULL}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int CmdHelp(const char *Cmd) {
|
|
|
|
|
(void)Cmd; // Cmd is not used so far
|
|
|
|
|
CmdsHelp(CommandTable);
|
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CmdHFWaveshare(const char *Cmd) {
|
|
|
|
|
clearCommandBuffer();
|
|
|
|
|
return CmdsParse(CommandTable, Cmd);
|
|
|
|
|
}
|