fix one too small
[RRG-proxmark3.git] / client / src / cmdhfwaveshare.c
blobc4ede2b3c47439383b3a0e649fcdc63b09171654
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Waveshare commands
17 //-----------------------------------------------------------------------------
18 // from ST25R3911B-NFC-Demo source code by Waveshare team
20 #include "cmdhfwaveshare.h"
22 #include <stdio.h>
23 #include <ctype.h>
24 #include "comms.h"
25 #include "cmdparser.h"
26 #include "ui.h"
27 #include "util.h"
28 #include "fileutils.h"
29 #include "util_posix.h" // msleep
30 #include "cliparser.h"
31 #include "imgutils.h"
33 #define EPD_1IN54B 0
34 #define EPD_1IN54C 1
35 #define EPD_1IN54V2 2
36 #define EPD_1IN54BCV2 3
37 #define EPD_2IN13V2 4
38 #define EPD_2IN13BC 5
39 #define EPD_2IN13D 6
40 #define EPD_2IN9 7
41 #define EPD_2IN9BC 8
42 #define EPD_2IN9D 9
43 #define EPD_4IN2 10
44 #define EPD_4IN2BC 11
45 #define EPD_7IN5 12
46 #define EPD_7IN5BC 13
47 #define EPD_7IN5V2 14
48 #define EPD_7IN5BCV2 15
49 #define EPD_2IN7 16
50 #define EPD_7IN5HD 17
52 typedef struct model_s {
53 const char *desc;
54 uint8_t len; // The data sent in one time shall not be greater than 128-3
55 uint16_t width;
56 uint16_t height;
57 } model_t;
59 typedef enum {
60 M2in13 = 0,
61 M2in9,
62 M4in2,
63 M7in5,
64 M2in7,
65 M2in13B,
66 M1in54B,
67 M7in5HD,
68 MEND
69 } model_enum_t;
71 static model_t models[] = {
72 {"2.13 inch e-paper", 16, 122, 250}, // tested
73 {"2.9 inch e-paper", 16, 296, 128},
74 {"4.2 inch e-paper", 100, 400, 300}, // tested
75 {"7.5 inch e-paper", 120, 800, 480},
76 {"2.7 inch e-paper", 121, 176, 276}, // tested
77 {"2.13 inch e-paper B (with red)", 106, 104, 212}, // tested
78 {"1.54 inch e-paper B (with red)", 100, 200, 200}, // tested
79 {"7.5 inch e-paper HD", 120, 880, 528},
82 static int CmdHelp(const char *Cmd);
84 static uint8_t *map8to1(gdImagePtr img, int color) {
85 // Calculate width rounding up
86 uint16_t width8 = (gdImageSX(img) + 7) / 8;
88 uint8_t *colormap8 = calloc(width8 * gdImageSY(img), sizeof(uint8_t));
89 if (!colormap8) {
90 return NULL;
93 uint8_t data = 0;
94 uint8_t count = 0;
95 for (uint16_t Y = 0; Y < gdImageSY(img); Y++) {
96 for (uint16_t X = 0; X < gdImageSX(img); X++) {
97 if (gdImageGetPixel(img, X, Y) == color) {
98 data |= 1;
100 count += 1;
101 if ((count >= 8) || (X == gdImageSX(img) - 1)) {
102 colormap8[X / 8 + Y * width8] = (~data) & 0xFF;
103 count = 0;
104 data = 0;
106 data = (data << 1) & 0xFF;
110 return colormap8;
113 static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, const uint8_t *black) {
114 for (uint8_t j = 0; j < models[model_nr].len; j++) {
115 l[3 + j] = black[i * models[model_nr].len + j];
118 static void read_red(uint32_t i, uint8_t *l, uint8_t model_nr, const uint8_t *red) {
119 // spurious warning with GCC10 (-Wstringop-overflow) when j is uint8_t, even if all len are < 128
120 for (uint16_t j = 0; j < models[model_nr].len; j++) {
121 if (model_nr == M1in54B) {
122 //1.54B needs to flip the red picture data, other screens do not need to flip data
123 l[3 + j] = ~red[i * models[model_nr].len + j];
124 } else {
125 l[3 + j] = red[i * models[model_nr].len + j];
130 static int transceive_blocking(uint8_t *txBuf, uint16_t txBufLen, uint8_t *rxBuf, uint16_t rxBufLen, uint16_t *actLen, bool retransmit) {
131 uint8_t fail_num = 0;
132 if (rxBufLen < 2) {
133 return PM3_EINVARG;
136 while (1) {
137 PacketResponseNG resp;
138 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, txBufLen, 0, txBuf, txBufLen);
139 rxBuf[0] = 1;
140 if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
141 if (resp.oldarg[0] > rxBufLen) {
142 PrintAndLogEx(WARNING, "Received %"PRIu64 " bytes, rxBuf too small (%u)", resp.oldarg[0], rxBufLen);
143 memcpy(rxBuf, resp.data.asBytes, rxBufLen);
144 *actLen = rxBufLen;
145 return PM3_ESOFT;
147 memcpy(rxBuf, resp.data.asBytes, resp.oldarg[0]);
148 *actLen = resp.oldarg[0];
151 if ((retransmit) && (rxBuf[0] != 0 || rxBuf[1] != 0)) {
152 fail_num++;
153 if (fail_num > 10) {
154 PROMPT_CLEARLINE;
155 PrintAndLogEx(WARNING, "Transmission failed, please try again.");
156 DropField();
157 return PM3_ESOFT;
159 } else {
160 break;
163 return PM3_SUCCESS;
166 // 1.54B Keychain
167 // 1.54B does not share the common base and requires specific handling
168 static int start_drawing_1in54B(uint8_t model_nr, uint8_t *black, uint8_t *red) {
169 int ret;
170 uint8_t step_5[128] = {0xcd, 0x05, 100};
171 uint8_t step_4[2] = {0xcd, 0x04};
172 uint8_t step_6[2] = {0xcd, 0x06};
173 uint8_t rx[20] = {0};
174 uint16_t actrxlen[20], i, progress;
176 if (model_nr == M1in54B) {
177 step_5[2] = 100;
179 PrintAndLogEx(DEBUG, "1.54_Step9: e-paper config2 (black)");
180 if (model_nr == M1in54B) { //1.54inch B Keychain
181 for (i = 0; i < 50; i++) {
182 read_black(i, step_5, model_nr, black);
183 ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
184 if (ret != PM3_SUCCESS) {
185 return ret;
187 progress = i * 100 / 100;
188 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
191 PROMPT_CLEARLINE;
192 PrintAndLogEx(DEBUG, "1.54_Step6: e-paper power on");
193 ret = transceive_blocking(step_4, 2, rx, 20, actrxlen, true); //cd 04
194 if (ret != PM3_SUCCESS) {
195 return ret;
198 PrintAndLogEx(DEBUG, "1.54_Step7: e-paper config2 (red)");
199 if (model_nr == M1in54B) { //1.54inch B Keychain
200 for (i = 0; i < 50; i++) {
201 read_red(i, step_5, model_nr, red);
202 ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
203 if (ret != PM3_SUCCESS) {
204 return ret;
206 progress = i * 100 / 100 + 50;
207 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
210 PROMPT_CLEARLINE;
211 // Send update instructions
212 PrintAndLogEx(DEBUG, "1.54_Step8: EDP load to main");
213 ret = transceive_blocking(step_6, 2, rx, 20, actrxlen, true); //cd 06
214 if (ret != PM3_SUCCESS) {
215 return ret;
218 PrintAndLogEx(DEBUG, "1.54_Step9");
219 return PM3_SUCCESS;
222 static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) {
223 uint8_t step0[2] = {0xcd, 0x0d};
224 uint8_t step1[3] = {0xcd, 0x00, 10}; // select e-paper type and reset e-paper
225 // 4 :2.13inch e-Paper
226 // 7 :2.9inch e-Paper
227 // 10 :4.2inch e-Paper
228 // 14 :7.5inch e-Paper
229 uint8_t step2[2] = {0xcd, 0x01}; // e-paper normal mode type:
230 uint8_t step3[2] = {0xcd, 0x02}; // e-paper config1
231 uint8_t step4[2] = {0xcd, 0x03}; // e-paper power on
232 uint8_t step5[2] = {0xcd, 0x05}; // e-paper config2
233 uint8_t step6[2] = {0xcd, 0x06}; // EDP load to main
234 uint8_t step7[3] = {0xcd, 0x07, 0}; // Data preparation
236 uint8_t step8[123] = {0xcd, 0x08, 0x64}; // Data start command
237 // 2.13inch(0x10:Send 16 data at a time)
238 // 2.9inch(0x10:Send 16 data at a time)
239 // 4.2inch(0x64:Send 100 data at a time)
240 // 7.5inch(0x78:Send 120 data at a time)
241 uint8_t step9[2] = {0xcd, 0x18}; // e-paper power on
242 uint8_t step10[2] = {0xcd, 0x09}; // Refresh e-paper
243 uint8_t step11[2] = {0xcd, 0x0a}; // wait for ready
244 uint8_t step12[2] = {0xcd, 0x04}; // e-paper power off command
245 uint8_t step13[124] = {0xcd, 0x19, 121};
246 // uint8_t step13[2]={0xcd,0x0b}; // Judge whether the power supply is turned off successfully
247 // uint8_t step14[2]={0xcd,0x0c}; // The end of the transmission
248 uint8_t rx[20];
249 uint16_t actrxlen[20];
251 clearCommandBuffer();
252 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
253 PacketResponseNG resp;
254 if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
255 PrintAndLogEx(ERR, "No tag found");
256 DropField();
257 return PM3_ETIMEOUT;
260 iso14a_card_select_t card;
261 memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
263 uint64_t select_status = resp.oldarg[0];
265 if (select_status == 0) {
266 PrintAndLogEx(ERR, "Tag select error");
267 DropField();
268 return PM3_ERFTRANS;
269 } else if (select_status == 3) {
270 PrintAndLogEx(WARNING, "Card doesn't support standard iso14443-3 anticollision, doesn't look like Waveshare tag");
271 DropField();
272 return PM3_ESOFT;
275 if ((card.uidlen != 7) || ((memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "FSTN11m", 7) != 0) && (memcmp(card.uid, "WSDZ10m", 7) != 0))) {
276 PrintAndLogEx(WARNING, "Card doesn't look like Waveshare tag");
277 DropField();
278 return PM3_ESOFT;
280 if (((model_nr != M1in54B) && ((memcmp(card.uid, "FSTN10m", 7) == 0) || (memcmp(card.uid, "FSTN11m", 7) == 0)))) {
281 PrintAndLogEx(WARNING, "Card is a Waveshare tag 1.54\", not %s", models[model_nr].desc);
282 DropField();
283 return PM3_ESOFT;
285 if (((model_nr == M1in54B) && (memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "FSTN11m", 7) != 0))) {
286 PrintAndLogEx(WARNING, "Card is not a Waveshare tag 1.54\", check your model number");
287 DropField();
288 return PM3_ESOFT;
290 PrintAndLogEx(DEBUG, "model_nr = %d", model_nr);
292 PrintAndLogEx(DEBUG, "Step0");
293 int ret = transceive_blocking(step0, 2, rx, 20, actrxlen, true); //cd 0d
294 if (ret != PM3_SUCCESS) {
295 return ret;
298 PrintAndLogEx(DEBUG, "Step1: e-paper config");
299 // step1[2] screen model
300 // step8[2] nr of bytes sent at once
301 // step13[2] nr of bytes sent for the second time
302 // generally, step8 sends a black image, step13 sends a red image
303 if (model_nr == M2in13) { // 2.13inch
304 step1[2] = EPD_2IN13V2;
305 step8[2] = 16;
306 step13[2] = 0;
307 } else if (model_nr == M2in9) { // 2.9inch
308 step1[2] = EPD_2IN9;
309 step8[2] = 16;
310 step13[2] = 0;
311 } else if (model_nr == M4in2) { // 4.2inch
312 step1[2] = EPD_4IN2;
313 step8[2] = 100;
314 step13[2] = 0;
315 } else if (model_nr == M7in5) { // 7.5inch
316 step1[2] = EPD_7IN5V2;
317 step8[2] = 120;
318 step13[2] = 0;
319 } else if (model_nr == M2in7) { // 2.7inch
320 step1[2] = EPD_2IN7;
321 step8[2] = 121;
322 // Send blank data for the first time, and send other data to 0xff without processing the bottom layer
323 step13[2] = 121;
324 // Sending the second data is the real image data. If the previous 0xff is not sent, the last output image is abnormally black
325 } else if (model_nr == M2in13B) { // 2.13inch B
326 step1[2] = EPD_2IN13BC;
327 step8[2] = 106;
328 step13[2] = 106;
329 } else if (model_nr == M7in5HD) {
330 step1[2] = EPD_7IN5HD;
331 step8[2] = 120;
332 step13[2] = 0;
335 if (model_nr == M1in54B) {
336 ret = transceive_blocking(step1, 2, rx, 20, actrxlen, true); // cd 00
337 } else {
338 ret = transceive_blocking(step1, 3, rx, 20, actrxlen, true);
340 if (ret != PM3_SUCCESS) {
341 return ret;
344 msleep(100);
345 PrintAndLogEx(DEBUG, "Step2: e-paper normal mode type");
346 ret = transceive_blocking(step2, 2, rx, 20, actrxlen, true); // cd 01
347 if (ret != PM3_SUCCESS) {
348 return ret;
351 msleep(100);
352 PrintAndLogEx(DEBUG, "Step3: e-paper config1");
353 ret = transceive_blocking(step3, 2, rx, 20, actrxlen, true); // cd 02
354 if (ret != PM3_SUCCESS) {
355 return ret;
358 msleep(200);
359 PrintAndLogEx(DEBUG, "Step4: e-paper power on");
360 ret = transceive_blocking(step4, 2, rx, 20, actrxlen, true); // cd 03
361 if (ret != PM3_SUCCESS) {
362 return ret;
365 if (model_nr == M1in54B) {
366 // 1.54B Keychain handler
367 PrintAndLogEx(DEBUG, "Start_Drawing_1in54B");
368 ret = start_drawing_1in54B(model_nr, black, red);
369 if (ret != PM3_SUCCESS) {
370 return ret;
372 // 1.54B Data transfer is complete and wait for refresh
373 } else {
374 uint8_t progress;
375 PrintAndLogEx(DEBUG, "Step5: e-paper config2");
376 ret = transceive_blocking(step5, 2, rx, 20, actrxlen, true); // cd 05
377 if (ret != PM3_SUCCESS) {
378 return ret;
380 msleep(100);
381 PrintAndLogEx(DEBUG, "Step6: EDP load to main") ;
382 ret = transceive_blocking(step6, 2, rx, 20, actrxlen, true); // cd 06
383 if (ret != PM3_SUCCESS) {
384 return ret;
386 msleep(100);
387 PrintAndLogEx(DEBUG, "Step7: Data preparation");
388 ret = transceive_blocking(step7, 3, rx, 20, actrxlen, true); // cd 07
389 if (ret != PM3_SUCCESS) {
390 return ret;
392 PrintAndLogEx(DEBUG, "Step8: Start data transfer");
393 if (model_nr == M2in13) { // 2.13inch
394 for (uint16_t i = 0; i < 250; i++) {
395 read_black(i, step8, model_nr, black);
396 ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
397 if (ret != PM3_SUCCESS) {
398 return ret;
400 progress = i * 100 / 250;
401 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
403 } else if (model_nr == M2in9) {
404 for (uint16_t i = 0; i < 296; i++) {
405 read_black(i, step8, model_nr, black);
406 ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
407 if (ret != PM3_SUCCESS) {
408 return ret;
410 progress = i * 100 / 296;
411 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
413 } else if (model_nr == M4in2) { //4.2inch
414 for (uint16_t i = 0; i < 150; i++) {
415 read_black(i, step8, model_nr, black);
416 ret = transceive_blocking(step8, 103, rx, 20, actrxlen, true); // cd 08
417 if (ret != PM3_SUCCESS) {
418 return ret;
420 progress = i * 100 / 150;
421 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
423 } else if (model_nr == M7in5) { //7.5inch
424 for (uint16_t i = 0; i < 400; i++) {
425 read_black(i, step8, model_nr, black);
426 ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
427 if (ret != PM3_SUCCESS) {
428 return ret;
430 progress = i * 100 / 400;
431 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
432 msleep(6);
434 } else if (model_nr == M2in13B) { //2.13inch B
435 for (uint16_t i = 0; i < 26; i++) {
436 read_black(i, step8, model_nr, black);
437 ret = transceive_blocking(step8, 109, rx, 20, actrxlen, false); // cd 08
438 if (ret != PM3_SUCCESS) {
439 return ret;
441 progress = i * 50 / 26;
442 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
444 } else if (model_nr == M7in5HD) { //7.5HD
446 for (uint16_t i = 0; i < 484; i++) {
447 read_black(i, step8, model_nr, black);
448 //memset(&step8[3], 0xf0, 120);
449 ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
450 if (ret != PM3_SUCCESS) {
451 return ret;
453 progress = i * 100 / 484;
454 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
456 memset(&step8[3], 0xff, 120);
457 ret = transceive_blocking(step8, 110 + 3, rx, 20, actrxlen, true); // cd 08
458 if (ret != PM3_SUCCESS) {
459 return ret;
461 } else if (model_nr == M2in7) { //2.7inch
462 for (uint16_t i = 0; i < 48; i++) {
463 //read_black(i,step8, model_nr, black);
464 memset(&step8[3], 0xFF, sizeof(step8) - 3);
465 ret = transceive_blocking(step8, 124, rx, 20, actrxlen, true); // cd 08
466 if (ret != PM3_SUCCESS) {
467 return ret;
469 progress = i * 50 / 48;
470 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
473 PROMPT_CLEARLINE;
474 PrintAndLogEx(DEBUG, "Step9: e-paper power on");
475 if (model_nr == M2in13 || model_nr == M2in9 || model_nr == M4in2 || model_nr == M7in5 || model_nr == M7in5HD) {
476 ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
477 // The black-and-white screen sending backplane is also shielded, with no effect. Except 2.7
478 if (ret != PM3_SUCCESS) {
479 return ret;
481 } else if (model_nr == M2in13B || model_nr == M2in7) {
482 ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
483 if (ret != PM3_SUCCESS) {
484 return ret;
486 PrintAndLogEx(DEBUG, "Step9b");
487 if (model_nr == M2in7) {
488 for (uint16_t i = 0; i < 48; i++) {
489 read_black(i, step13, model_nr, black);
490 ret = transceive_blocking(step13, 124, rx, 20, actrxlen, true); //CD 19
491 if (ret != PM3_SUCCESS) {
492 return ret;
494 progress = i * 50 / 48 + 50;
495 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
497 } else if (model_nr == M2in13B) {
498 for (uint16_t i = 0; i < 26; i++) {
499 read_red(i, step13, model_nr, red);
500 //memset(&step13[3], 0xfE, 106);
501 ret = transceive_blocking(step13, 109, rx, 20, actrxlen, false);
502 if (ret != PM3_SUCCESS) {
503 return ret;
505 progress = i * 50 / 26 + 50;
506 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
509 PROMPT_CLEARLINE;
511 PrintAndLogEx(DEBUG, "Step10: Refresh e-paper");
512 ret = transceive_blocking(step10, 2, rx, 20, actrxlen, true); //cd 09 refresh command
513 if (ret != PM3_SUCCESS) {
514 return ret;
516 msleep(200);
518 PrintAndLogEx(DEBUG, "Step11: Wait tag to be ready");
519 PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
520 if (model_nr == M2in13B || model_nr == M1in54B) { // Black, white and red screen refresh time is longer, wait first
521 msleep(9000);
522 } else if (model_nr == M7in5HD) {
523 msleep(1000);
526 uint8_t fail_num = 0;
527 while (1) {
528 if (model_nr == M1in54B) {
529 // send 0xcd 0x08 with 1.54B
530 ret = transceive_blocking(step8, 2, rx, 20, actrxlen, false); //cd 08
531 } else {
532 ret = transceive_blocking(step11, 2, rx, 20, actrxlen, false); //cd 0a
534 if (ret != PM3_SUCCESS) {
535 return ret;
537 if (rx[0] == 0xff && rx[1] == 0) {
538 PrintAndLogEx(NORMAL, "");
539 PrintAndLogEx(SUCCESS, "E-paper Reflash OK");
540 msleep(200);
541 break;
542 } else {
543 if (fail_num > 50) {
544 PrintAndLogEx(WARNING, "Update failed, please try again.");
545 DropField();
546 return PM3_ESOFT;
547 } else {
548 fail_num++;
549 PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
550 msleep(400);
554 PrintAndLogEx(DEBUG, "Step12: e-paper power off command");
555 ret = transceive_blocking(step12, 2, rx, 20, actrxlen, true); //cd 04
556 if (ret != PM3_SUCCESS) {
557 return ret;
559 msleep(200);
560 PrintAndLogEx(SUCCESS, "E-paper Update OK");
561 msleep(200);
562 DropField();
563 return PM3_SUCCESS;
566 static int CmdHF14AWSLoad(const char *Cmd) {
568 char desc[800] = {0};
569 for (uint8_t i = 0; i < MEND; i++) {
570 snprintf(desc + strlen(desc),
571 sizeof(desc) - strlen(desc),
572 "hf waveshare load -f myfile -m %2u -> %s ( %u, %u )\n",
574 models[i].desc,
575 models[i].width,
576 models[i].height
580 CLIParserContext *ctx;
581 CLIParserInit(&ctx, "hf waveshare load",
582 "Load image file to Waveshare NFC ePaper",
583 desc
586 char modeldesc[40];
587 snprintf(modeldesc, sizeof(modeldesc), "model number [0 - %d] of your tag", MEND - 1);
589 void *argtable[] = {
590 arg_param_begin,
591 arg_int1("m", NULL, "<nr>", modeldesc),
592 arg_str1("f", "file", "<fn>", "specify image to upload to tag"),
593 arg_str0("s", "save", "<fn>", "save paletized version in file"),
594 arg_param_end
597 CLIExecWithReturn(ctx, Cmd, argtable, false);
599 int model_nr = arg_get_int_def(ctx, 1, -1);
601 int infilelen, outfilelen;
602 char infile[FILE_PATH_SIZE];
603 char outfile[FILE_PATH_SIZE];
604 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)infile, FILE_PATH_SIZE, &infilelen);
605 CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)outfile, FILE_PATH_SIZE, &outfilelen);
606 CLIParserFree(ctx);
608 //Validations
609 if (infilelen < 1) {
610 PrintAndLogEx(WARNING, "Missing input file");
611 return PM3_EINVARG;
613 if (model_nr == -1) {
614 PrintAndLogEx(WARNING, "Missing model");
615 return PM3_EINVARG;
617 if (model_nr >= MEND) {
618 PrintAndLogEx(WARNING, "Unknown model");
619 return PM3_EINVARG;
621 if (!g_session.pm3_present && !outfilelen) {
622 PrintAndLogEx(WARNING, "Offline - can only perform image conversion");
623 return PM3_ENOTTY;
626 bool model_has_red = model_nr == M1in54B || model_nr == M2in13B;
628 gdImagePtr rgb_img = gdImageCreateFromFile(infile);
629 if (!rgb_img) {
630 PrintAndLogEx(WARNING, "Could not load image from " _YELLOW_("%s"), infile);
631 return PM3_EFILE;
634 gdImagePtr scaled_img = img_crop_to_fit(rgb_img, models[model_nr].width, models[model_nr].height);
635 if (scaled_img == NULL) {
636 PrintAndLogEx(WARNING, "Failed to scale input image");
637 gdImageDestroy(rgb_img);
638 return PM3_EFILE;
640 gdImageDestroy(rgb_img);
642 int pal_len = 2;
643 int pal[3];
644 pal[0] = gdTrueColorAlpha(0xFF, 0xFF, 0xFF, 0); // White
645 pal[1] = gdTrueColorAlpha(0x00, 0x00, 0x00, 0); // Black
646 if (model_has_red) {
647 pal_len = 3;
648 pal[2] = gdTrueColorAlpha(0xFF, 0x00, 0x00, 0); // Red
651 gdImagePtr pal_img = img_palettize(scaled_img, pal, pal_len);
652 gdImageDestroy(scaled_img);
654 if (!pal_img) {
655 PrintAndLogEx(WARNING, "Could not convert image");
656 return PM3_EMALLOC;
659 if (outfilelen) {
660 if (gdImageFile(pal_img, outfile)) {
661 PrintAndLogEx(INFO, "Save converted image to " _YELLOW_("%s"), outfile);
662 gdImageDestroy(pal_img);
663 return PM3_SUCCESS;
664 } else {
665 PrintAndLogEx(WARNING, "Could not save converted image", outfile);
666 gdImageDestroy(pal_img);
667 return PM3_EFILE;
671 uint8_t *black_plane = map8to1(pal_img, 1);
672 if (!black_plane) {
673 PrintAndLogEx(WARNING, "Could not convert image to bit plane");
674 gdImageDestroy(pal_img);
675 return PM3_EMALLOC;
678 uint8_t *red_plane = NULL;
679 if (model_has_red) {
680 red_plane = map8to1(pal_img, 2);
681 if (!red_plane) {
682 PrintAndLogEx(WARNING, "Could not convert image to bit plane");
683 free(black_plane);
684 gdImageDestroy(pal_img);
685 return PM3_EMALLOC;
688 gdImageDestroy(pal_img);
690 int res = start_drawing(model_nr, black_plane, red_plane);
691 free(black_plane);
692 free(red_plane);
693 return res;
696 static command_t CommandTable[] = {
697 {"help", CmdHelp, AlwaysAvailable, "This help"},
698 {"load", CmdHF14AWSLoad, AlwaysAvailable, "Load image file to Waveshare NFC ePaper"},
699 {NULL, NULL, NULL, NULL}
702 static int CmdHelp(const char *Cmd) {
703 (void)Cmd; // Cmd is not used so far
704 CmdsHelp(CommandTable);
705 return PM3_SUCCESS;
708 int CmdHFWaveshare(const char *Cmd) {
709 clearCommandBuffer();
710 return CmdsParse(CommandTable, Cmd);