fix one too small
[RRG-proxmark3.git] / client / src / ui.c
blobb682dee4921df9df6bac3fc9b6976c3ec6383781
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 // UI utilities
17 //-----------------------------------------------------------------------------
19 /* Ensure strtok_r is available even with -std=c99; must be included before
21 #if !defined(_WIN32)
22 #define _POSIX_C_SOURCE 200112L
23 #endif
24 #include "ui.h"
25 #include "commonutil.h" // ARRAYLEN
26 #include <stdio.h> // for Mingw readline
27 #include <stdarg.h>
28 #include <stdlib.h>
30 #if defined(HAVE_READLINE)
31 //Load readline after stdio.h
32 #include <readline/readline.h>
33 #endif
35 #include <complex.h>
36 #include "util.h"
37 #include "proxmark3.h" // PROXLOG
38 #include "fileutils.h"
39 #include "pm3_cmd.h"
41 #ifdef _WIN32
42 # include <direct.h> // _mkdir
43 #endif
45 #include <time.h>
46 #include "emojis.h"
47 #include "emojis_alt.h"
48 session_arg_t g_session;
50 double g_CursorScaleFactor = 1;
51 char g_CursorScaleFactorUnit[11] = {0};
52 double g_PlotGridX = 0, g_PlotGridY = 0;
53 double g_DefaultGridX = 64, g_DefaultGridY = 64;
54 uint32_t g_GraphStart = 0; // Starting point/offset for the left side of the graph
55 uint32_t g_GraphStop = 0;
56 uint32_t g_GraphStart_old = 0;
57 double g_GraphPixelsPerPoint = 1.f; // How many visual pixels are between each sample point (x axis)
58 static bool flushAfterWrite = false;
59 double g_GridOffset = 0;
60 bool g_GridLocked = false;
62 pthread_mutex_t g_print_lock = PTHREAD_MUTEX_INITIALIZER;
64 static void fPrintAndLog(FILE *stream, const char *fmt, ...);
66 #ifdef _WIN32
67 #define MKDIR_CHK _mkdir(path)
68 #else
69 #define MKDIR_CHK mkdir(path, 0700)
70 #endif
73 // needed by flasher, so let's put it here instead of fileutils.c
74 int searchHomeFilePath(char **foundpath, const char *subdir, const char *filename, bool create_home) {
75 if (foundpath == NULL) {
76 return PM3_EINVARG;
79 const char *user_path = get_my_user_directory();
80 if (user_path == NULL) {
81 fprintf(stderr, "Could not retrieve $HOME from the environment\n");
82 return PM3_EFILE;
85 size_t pathlen = strlen(user_path) + strlen(PM3_USER_DIRECTORY) + 1;
86 char *path = calloc(pathlen, sizeof(char));
87 if (path == NULL) {
88 return PM3_EMALLOC;
91 strcpy(path, user_path);
92 strcat(path, PM3_USER_DIRECTORY);
93 int result;
95 #ifdef _WIN32
96 struct _stat st;
97 // Mingw _stat fails if path ends with /, so let's use a stripped path
98 if (str_endswith(path, PATHSEP)) {
99 memset(path + (strlen(path) - strlen(PATHSEP)), 0x00, strlen(PATHSEP));
100 result = _stat(path, &st);
101 strcat(path, PATHSEP);
102 } else {
103 result = _stat(path, &st);
105 #else
106 struct stat st;
107 result = stat(path, &st);
108 #endif
110 if ((result != 0) && create_home) {
112 if (MKDIR_CHK) {
113 fprintf(stderr, "Could not create user directory %s\n", path);
114 free(path);
115 return PM3_EFILE;
119 if (subdir != NULL) {
120 pathlen += strlen(subdir);
121 char *tmp = realloc(path, pathlen * sizeof(char));
122 if (tmp == NULL) {
123 free(path);
124 return PM3_EMALLOC;
126 path = tmp;
127 strcat(path, subdir);
129 #ifdef _WIN32
130 // Mingw _stat fails if path ends with /, so let's use a stripped path
131 if (str_endswith(path, PATHSEP)) {
132 memset(path + (strlen(path) - strlen(PATHSEP)), 0x00, strlen(PATHSEP));
133 result = _stat(path, &st);
134 strcat(path, PATHSEP);
135 } else {
136 result = _stat(path, &st);
138 #else
139 result = stat(path, &st);
140 #endif
142 if ((result != 0) && create_home) {
144 if (MKDIR_CHK) {
145 fprintf(stderr, "Could not create user directory %s\n", path);
146 free(path);
147 return PM3_EFILE;
152 if (filename == NULL) {
153 *foundpath = path;
154 return PM3_SUCCESS;
157 pathlen += strlen(filename);
158 char *tmp = realloc(path, pathlen * sizeof(char));
159 if (tmp == NULL) {
160 free(path);
161 return PM3_EMALLOC;
164 path = tmp;
165 strcat(path, filename);
166 *foundpath = path;
168 return PM3_SUCCESS;
171 void free_grabber(void) {
172 free(g_grabbed_output.ptr);
173 g_grabbed_output.ptr = NULL;
174 g_grabbed_output.size = 0;
175 g_grabbed_output.idx = 0;
178 static void fill_grabber(const char *string) {
179 if (g_grabbed_output.ptr == NULL || g_grabbed_output.size - g_grabbed_output.idx < MAX_PRINT_BUFFER) {
180 char *tmp = realloc(g_grabbed_output.ptr, g_grabbed_output.size + MAX_PRINT_BUFFER);
181 if (tmp == NULL) {
182 // We leave current g_grabbed_output untouched
183 PrintAndLogEx(ERR, "Out of memory error in fill_grabber()");
184 return;
186 g_grabbed_output.ptr = tmp;
187 g_grabbed_output.size += MAX_PRINT_BUFFER;
189 int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
190 if (len < 0 || len > MAX_PRINT_BUFFER) {
191 // We leave current g_grabbed_output_len untouched
192 PrintAndLogEx(ERR, "snprintf error in fill_grabber()");
193 return;
195 g_grabbed_output.idx += len;
198 void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
199 char buff[2000] = "Options:\n";
200 char format[2000] = "";
201 size_t counts[2] = {0, 0};
202 for (size_t i = 0; i < size; i++)
203 for (size_t j = 0 ; j < 2 ; j++)
204 if (counts[j] < strlen(str[i][j])) {
205 counts[j] = strlen(str[i][j]);
207 for (size_t i = 0; i < size; i++) {
208 for (size_t j = 0; j < 2; j++) {
209 if (j == 0)
210 snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
211 else
212 snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
213 snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
215 if (i < size - 1)
216 strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
218 PrintAndLogEx(NORMAL, "%s", buff);
221 static uint8_t PrintAndLogEx_spinidx = 0;
223 void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
225 // skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
226 if (g_debugMode == 0 && level == DEBUG)
227 return;
229 // skip HINT messages if client has hints turned off i.e. 'HINT 0'
230 if (g_session.show_hints == false && level == HINT)
231 return;
233 char prefix[40] = {0};
234 char buffer[MAX_PRINT_BUFFER] = {0};
235 char buffer2[MAX_PRINT_BUFFER + sizeof(prefix)] = {0};
236 char *token = NULL;
237 char *tmp_ptr = NULL;
238 FILE *stream = stdout;
239 const char *spinner[] = {_YELLOW_("[\\]"), _YELLOW_("[|]"), _YELLOW_("[/]"), _YELLOW_("[-]")};
240 const char *spinner_emoji[] = {" :clock1: ", " :clock2: ", " :clock3: ", " :clock4: ", " :clock5: ", " :clock6: ",
241 " :clock7: ", " :clock8: ", " :clock9: ", " :clock10: ", " :clock11: ", " :clock12: "
243 switch (level) {
244 case ERR:
245 if (g_session.emoji_mode == EMO_EMOJI)
246 strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
247 else
248 strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
249 stream = stderr;
250 break;
251 case FAILED:
252 if (g_session.emoji_mode == EMO_EMOJI)
253 strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
254 else
255 strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
256 break;
257 case DEBUG:
258 strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
259 break;
260 case HINT:
261 strncpy(prefix, "[" _YELLOW_("?") "] ", sizeof(prefix) - 1);
262 break;
263 case SUCCESS:
264 strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
265 break;
266 case WARNING:
267 if (g_session.emoji_mode == EMO_EMOJI)
268 strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
269 else
270 strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
271 break;
272 case INFO:
273 strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
274 break;
275 case INPLACE:
276 if (g_session.emoji_mode == EMO_EMOJI) {
277 strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
278 PrintAndLogEx_spinidx++;
279 if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji))
280 PrintAndLogEx_spinidx = 0;
281 } else {
282 strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
283 PrintAndLogEx_spinidx++;
284 if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner))
285 PrintAndLogEx_spinidx = 0;
287 break;
288 case NORMAL:
289 // no prefixes for normal
290 break;
293 va_list args;
294 va_start(args, fmt);
295 vsnprintf(buffer, sizeof(buffer), fmt, args);
296 va_end(args);
298 // no prefixes for normal & inplace
299 if (level == NORMAL) {
300 fPrintAndLog(stream, "%s", buffer);
301 return;
304 if (strchr(buffer, '\n')) {
306 const char delim[2] = "\n";
308 // line starts with newline
309 if (buffer[0] == '\n')
310 fPrintAndLog(stream, "");
312 token = strtok_r(buffer, delim, &tmp_ptr);
314 while (token != NULL) {
316 size_t size = strlen(buffer2);
318 if (strlen(token))
319 snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
320 else
321 snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
323 token = strtok_r(NULL, delim, &tmp_ptr);
325 fPrintAndLog(stream, "%s", buffer2);
326 } else {
327 snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
328 if (level == INPLACE) {
329 // ignore INPLACE if rest of output is grabbed
330 if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
331 char buffer3[sizeof(buffer2)] = {0};
332 char buffer4[sizeof(buffer2)] = {0};
333 memcpy_filter_ansi(buffer3, buffer2, sizeof(buffer2), !g_session.supports_colors);
334 memcpy_filter_emoji(buffer4, buffer3, sizeof(buffer3), g_session.emoji_mode);
335 fprintf(stream, "\r%s", buffer4);
336 fflush(stream);
338 } else {
339 fPrintAndLog(stream, "%s", buffer2);
344 static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
345 va_list argptr;
346 static FILE *logfile = NULL;
347 static int logging = 1;
348 char buffer[MAX_PRINT_BUFFER] = {0};
349 char buffer2[MAX_PRINT_BUFFER] = {0};
350 char buffer3[MAX_PRINT_BUFFER] = {0};
352 bool linefeed = true;
354 if (logging && g_session.incognito) {
355 logging = 0;
357 if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
358 char *my_logfile_path = NULL;
359 char filename[40];
360 struct tm *timenow;
361 time_t now = time(NULL);
362 timenow = gmtime(&now);
363 strftime(filename, sizeof(filename), PROXLOG, timenow);
364 if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
365 printf(_YELLOW_("[-]") " Logging disabled!\n");
366 my_logfile_path = NULL;
367 logging = 0;
368 } else {
369 logfile = fopen(my_logfile_path, "a");
370 if (logfile == NULL) {
371 printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
372 logging = 0;
373 } else {
375 if (g_session.supports_colors) {
376 printf("["_YELLOW_("=")"] Session log " _YELLOW_("%s") "\n", my_logfile_path);
377 } else {
378 printf("[=] Session log %s\n", my_logfile_path);
382 free(my_logfile_path);
386 // lock this section to avoid interlacing prints from different threads
387 pthread_mutex_lock(&g_print_lock);
389 // If there is an incoming message from the hardware (eg: lf hid read) in
390 // the background (while the prompt is displayed and accepting user input),
391 // stash the prompt and bring it back later.
392 #ifdef RL_STATE_READCMD
393 // We are using GNU readline. libedit (OSX) doesn't support this flag.
394 int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0;
395 char *saved_line;
396 int saved_point;
398 if (need_hack) {
399 saved_point = rl_point;
400 saved_line = rl_copy_text(0, rl_end);
401 rl_save_prompt();
402 rl_replace_line("", 0);
403 rl_redisplay();
405 #endif
407 va_start(argptr, fmt);
408 vsnprintf(buffer, sizeof(buffer), fmt, argptr);
409 va_end(argptr);
410 if (strlen(buffer) > 0 && buffer[strlen(buffer) - 1] == NOLF[0]) {
411 linefeed = false;
412 buffer[strlen(buffer) - 1] = 0;
414 bool filter_ansi = !g_session.supports_colors;
415 memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
416 if (g_printAndLog & PRINTANDLOG_PRINT) {
417 memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
418 fprintf(stream, "%s", buffer3);
419 if (linefeed)
420 fprintf(stream, "\n");
423 #ifdef RL_STATE_READCMD
424 // We are using GNU readline. libedit (OSX) doesn't support this flag.
425 if (need_hack) {
426 rl_restore_prompt();
427 rl_replace_line(saved_line, 0);
428 rl_point = saved_point;
429 rl_redisplay();
430 free(saved_line);
432 #endif
434 if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
435 (g_printAndLog & PRINTANDLOG_GRAB)) {
436 memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
437 if (!filter_ansi) {
438 memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
441 if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
442 if (filter_ansi) {
443 fprintf(logfile, "%s", buffer3);
444 } else {
445 fprintf(logfile, "%s", buffer);
447 if (linefeed)
448 fprintf(logfile, "\n");
449 fflush(logfile);
451 if (g_printAndLog & PRINTANDLOG_GRAB) {
452 if (filter_ansi) {
453 fill_grabber(buffer3);
454 } else {
455 fill_grabber(buffer);
457 if (linefeed)
458 fill_grabber("\n");
461 if (flushAfterWrite)
462 fflush(stdout);
464 //release lock
465 pthread_mutex_unlock(&g_print_lock);
468 void SetFlushAfterWrite(bool value) {
469 flushAfterWrite = value;
472 bool GetFlushAfterWrite(void) {
473 return flushAfterWrite;
476 void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n) {
477 uint8_t *rdest = (uint8_t *)dest;
478 uint8_t *rsrc = (uint8_t *)src;
479 uint16_t si = 0;
480 for (size_t i = 0; i < n; i++) {
481 if ((rsrc[i] == '\001') || (rsrc[i] == '\002'))
482 // skip readline special markers
483 continue;
484 rdest[si++] = rsrc[i];
488 void memcpy_filter_ansi(void *dest, const void *src, size_t n, bool filter) {
489 if (filter) {
490 // Filter out ANSI sequences on these OS
491 uint8_t *rdest = (uint8_t *)dest;
492 uint8_t *rsrc = (uint8_t *)src;
493 uint16_t si = 0;
494 for (size_t i = 0; i < n; i++) {
495 if ((i < n - 1)
496 && (rsrc[i] == '\x1b')
497 && (rsrc[i + 1] >= 0x40)
498 && (rsrc[i + 1] <= 0x5F)) { // entering ANSI sequence
500 i++;
501 if ((i < n - 1) && (rsrc[i] == '[')) { // entering CSI sequence
502 i++;
504 while ((i < n - 1) && (rsrc[i] >= 0x30) && (rsrc[i] <= 0x3F)) { // parameter bytes
505 i++;
508 while ((i < n - 1) && (rsrc[i] >= 0x20) && (rsrc[i] <= 0x2F)) { // intermediate bytes
509 i++;
512 if ((rsrc[i] >= 0x40) && (rsrc[i] <= 0x7F)) { // final byte
513 continue;
515 } else {
516 continue;
519 rdest[si++] = rsrc[i];
521 } else {
522 memcpy(dest, src, n);
526 static bool emojify_token(const char *token, uint8_t token_length, const char **emojified_token, uint8_t *emojified_token_length, emojiMode_t mode) {
527 int i = 0;
528 while (EmojiTable[i].alias && EmojiTable[i].emoji) {
529 if ((strlen(EmojiTable[i].alias) == token_length) && (0 == memcmp(EmojiTable[i].alias, token, token_length))) {
530 switch (mode) {
531 case EMO_EMOJI: {
532 *emojified_token = EmojiTable[i].emoji;
533 *emojified_token_length = strlen(EmojiTable[i].emoji);
534 break;
536 case EMO_ALTTEXT: {
537 int j = 0;
538 *emojified_token_length = 0;
539 while (EmojiAltTable[j].alias && EmojiAltTable[j].alttext) {
540 if ((strlen(EmojiAltTable[j].alias) == token_length) && (0 == memcmp(EmojiAltTable[j].alias, token, token_length))) {
541 *emojified_token = EmojiAltTable[j].alttext;
542 *emojified_token_length = strlen(EmojiAltTable[j].alttext);
543 break;
545 ++j;
547 break;
549 case EMO_NONE: {
550 *emojified_token_length = 0;
551 break;
553 case EMO_ALIAS: { // should never happen
554 return false;
557 return true;
559 ++i;
561 return false;
564 static bool token_charset(uint8_t c) {
565 if ((c >= '0') && (c <= '9')) return true;
566 if ((c >= 'a') && (c <= 'z')) return true;
567 if ((c >= 'A') && (c <= 'Z')) return true;
568 if ((c == '_') || (c == '+') || (c == '-')) return true;
569 return false;
572 void memcpy_filter_emoji(void *dest, const void *src, size_t n, emojiMode_t mode) {
573 if (mode == EMO_ALIAS) {
574 memcpy(dest, src, n);
575 } else {
576 // tokenize emoji
577 const char *emojified_token = NULL;
578 uint8_t emojified_token_length = 0;
579 char *current_token = NULL;
580 uint8_t current_token_length = 0;
581 char *rdest = (char *)dest;
582 char *rsrc = (char *)src;
583 uint16_t si = 0;
584 for (size_t i = 0; i < n; i++) {
585 char current_char = rsrc[i];
587 if (current_token_length == 0) {
588 // starting a new token.
589 if (current_char == ':') {
590 current_token = rsrc + i;
591 current_token_length = 1;
592 } else { // not starting a new token.
593 rdest[si++] = current_char;
595 } else {
596 // finishing the current token.
597 if (current_char == ':') {
598 // nothing changed? we still need the ending ':' as it might serve for an upcoming emoji
599 if (! emojify_token(current_token, current_token_length + 1, &emojified_token, &emojified_token_length, mode)) {
600 memcpy(rdest + si, current_token, current_token_length);
601 si += current_token_length;
602 current_token = rsrc + i;
603 current_token_length = 1;
604 } else {
605 memcpy(rdest + si, emojified_token, emojified_token_length);
606 si += emojified_token_length;
607 current_token_length = 0;
609 } else if (token_charset(current_char)) { // continuing the current token.
610 current_token_length++;
611 } else { // dropping the current token.
612 current_token_length++;
613 memcpy(rdest + si, current_token, current_token_length);
614 si += current_token_length;
615 current_token_length = 0;
619 if (current_token_length > 0) {
620 memcpy(rdest + si, current_token, current_token_length);
626 // If reactivated, beware it doesn't compile on Android (DXL)
627 void iceIIR_Butterworth(int *data, const size_t len) {
629 int *output = (int *) calloc(sizeof(int) * len, sizeof(uint8_t));
630 if (!output) return;
632 // clear mem
633 memset(output, 0x00, len);
635 size_t adjustedLen = len;
636 float fc = 0.1125f; // center frequency
638 // create very simple low-pass filter to remove images (2nd-order Butterworth)
639 float complex iir_buf[3] = {0, 0, 0};
640 float b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929};
641 float a[3] = {1.000000000000000, -1.822694925196308, 0.837181651256023};
643 for (size_t i = 0; i < adjustedLen; ++i) {
645 float sample = data[i]; // input sample read from array
646 float complex x_prime = 1.0f; // save sample for estimating frequency
647 float complex x;
649 // remove DC offset and mix to complex baseband
650 x = (sample - 127.5f) * cexpf(_Complex_I * 2 * M_PI * fc * i);
652 // apply low-pass filter, removing spectral image (IIR using direct-form II)
653 iir_buf[2] = iir_buf[1];
654 iir_buf[1] = iir_buf[0];
655 iir_buf[0] = x - a[1] * iir_buf[1] - a[2] * iir_buf[2];
656 x = b[0] * iir_buf[0] +
657 b[1] * iir_buf[1] +
658 b[2] * iir_buf[2];
660 // compute instantaneous frequency by looking at phase difference
661 // between adjacent samples
662 float freq = cargf(x * conjf(x_prime));
663 x_prime = x; // retain this sample for next iteration
665 output[i] = (freq > 0) ? 127 : -127;
668 // show data
669 //memcpy(data, output, adjustedLen);
670 for (size_t j = 0; j < adjustedLen; ++j)
671 data[j] = output[j];
673 free(output);
677 void iceSimple_Filter(int *data, const size_t len, uint8_t k) {
678 // ref: http://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications
679 // parameter K
680 #define FILTER_SHIFT 4
682 int32_t filter_reg = 0;
683 int8_t shift = (k <= 8) ? k : FILTER_SHIFT;
685 for (size_t i = 0; i < len; ++i) {
686 // Update filter with current sample
687 filter_reg = filter_reg - (filter_reg >> shift) + data[i];
689 // Scale output for unity gain
690 data[i] = filter_reg >> shift;
694 void print_progress(uint64_t count, uint64_t max, barMode_t style) {
695 int cols = 100 + 35;
696 max = (count > max) ? count : max;
697 #if defined(HAVE_READLINE)
698 static int prev_cols = 0;
699 int rows;
700 rl_reset_screen_size(); // refresh Readline idea of the actual screen width
701 rl_get_screen_size(&rows, &cols);
703 if (cols < 36)
704 return;
706 (void) rows;
707 if (prev_cols > cols) {
708 PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ "");
710 prev_cols = cols;
711 #endif
712 int width = cols - 35;
714 #define PERCENTAGE(V, T) ((V * width) / T)
715 // x/8 fractional part of the percentage
716 #define PERCENTAGEFRAC(V, T) ((uint8_t)(((((float)V * width) / T) - ((V * width) / T)) * 8))
718 const char *smoothtable[] = {
719 "\xe2\x80\x80",
720 "\xe2\x96\x8F",
721 "\xe2\x96\x8E",
722 "\xe2\x96\x8D",
723 "\xe2\x96\x8C",
724 "\xe2\x96\x8B",
725 "\xe2\x96\x8A",
726 "\xe2\x96\x89",
727 "\xe2\x96\x88",
730 int mode = (g_session.emoji_mode == EMO_EMOJI);
732 const char *block[] = {"#", "\xe2\x96\x88"};
733 // use a 3-byte space in emoji mode to ease computations
734 const char *space[] = {" ", "\xe2\x80\x80"};
736 size_t unit = strlen(block[mode]);
737 // +1 for \0
738 char *bar = (char *)calloc(unit * width + 1, sizeof(uint8_t));
740 uint8_t value = PERCENTAGE(count, max);
742 int i = 0;
743 // prefix is added already.
744 for (; i < unit * value; i += unit) {
745 memcpy(bar + i, block[mode], unit);
747 if (i < unit * width) {
748 // add last block
749 if (mode == 1) {
750 memcpy(bar + i, smoothtable[PERCENTAGEFRAC(count, max)], unit);
751 } else {
752 memcpy(bar + i, space[mode], unit);
754 i += unit;
756 // add spaces
757 for (; i < unit * width; i += unit) {
758 memcpy(bar + i, space[mode], unit);
760 // color buffer
761 size_t collen = strlen(bar) + 40;
762 char *cbar = (char *)calloc(collen, sizeof(uint8_t));
764 // Add colors
765 if (g_session.supports_colors) {
766 int p60 = unit * (width * 60 / 100);
767 int p20 = unit * (width * 20 / 100);
768 snprintf(cbar, collen, _GREEN_("%.*s"), p60, bar);
769 snprintf(cbar + strlen(cbar), collen - strlen(cbar), _CYAN_("%.*s"), p20, bar + p60);
770 snprintf(cbar + strlen(cbar), collen - strlen(cbar), _YELLOW_("%.*s"), (int)(unit * width - p60 - p20), bar + p60 + p20);
771 } else {
772 snprintf(cbar, collen, "%s", bar);
775 switch (style) {
776 case STYLE_BAR: {
777 printf("\b%c[2K\r[" _YELLOW_("=")"] %s", 27, cbar);
778 break;
780 case STYLE_MIXED: {
781 printf("\b%c[2K\r[" _YELLOW_("=")"] %s [ %"PRIu64" mV / %2u V / %2u Vmax ]", 27, cbar, count, (uint32_t)(count / 1000), (uint32_t)(max / 1000));
782 break;
784 case STYLE_VALUE: {
785 printf("[" _YELLOW_("=")"] %"PRIu64" mV / %2u V / %2u Vmax \r", count, (uint32_t)(count / 1000), (uint32_t)(max / 1000));
786 break;
789 fflush(stdout);
790 free(bar);
791 free(cbar);