From e42155973a8fccc76c28d6ee1810b25ce77dc338 Mon Sep 17 00:00:00 2001 From: Brian Barto Date: Tue, 17 Jan 2017 22:00:24 -0500 Subject: [PATCH] ncurses implementation modified: Makefile new file: src/nms-ncurses.c --- Makefile | 3 + src/nms-ncurses.c | 462 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 465 insertions(+) create mode 100644 src/nms-ncurses.c diff --git a/Makefile b/Makefile index 910e4ea..252e982 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ sneakers: $(OBJ)/nms.o $(OBJ)/sneakers.o | $(BIN) all: nms sneakers +nms-ncurses: $(OBJ)/nms-ncurses.o $(OBJ)/main.o | $(BIN) + $(CC) $(CFLAGS) -o $(BIN)/$@ $^ -lncurses + $(OBJ)/%.o: $(SRC)/%.c | $(OBJ) $(CC) $(CFLAGS) -o $@ -c $< diff --git a/src/nms-ncurses.c b/src/nms-ncurses.c new file mode 100644 index 0000000..6f6f640 --- /dev/null +++ b/src/nms-ncurses.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2017 Brian Barto + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the MIT License. See LICENSE for more details. + */ + +/* + * DESCRIPTION + * + * This library encapsulates the functionality required to produce the + * famous data decryption effect from the 1992 hacker movie Sneakers. + */ + +#define _XOPEN_SOURCE 700 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nms.h" + +// Color identifiers +/* +#define COLOR_BLACK 0 +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 +*/ + +// Macros for VT100 codes +/* +#define CLEAR_SCR() printf("\033[2J") // Clear Screen +#define CURSOR_HOME() printf("\033[H") // Move cursor to home position (0,0) +#define CURSOR_MOVE(x,y) printf("\033[%i;%iH", x, y) // Move cursor to x,y +#define BEEP() printf("\a"); // terminal bell +#define BOLD() printf("\033[1m") // Cursor bold +#define FOREGROUND_COLOR(x) printf("\033[3%im", x) // Set foreground color +#define CLEAR_ATTR() printf("\033[0m") // Clear bold/color attributes +#define SCREEN_SAVE() printf("\033[?47h") // Save screen display +#define SCREEN_RESTORE() printf("\033[?47l") // Restore screen to previously saved state +#define CURSOR_SAVE() printf("\033[s") // Save cursor position +#define CURSOR_RESTORE() printf("\033[u") // Restore cursor position +#define CURSOR_HIDE() printf("\033[?25l") // Hide cursor +#define CURSOR_SHOW() printf("\033[?25h") // Unhide cursor +*/ + +// Program settings +#define TYPE_EFFECT_SPEED 4 // miliseconds per char +#define JUMBLE_SECONDS 2 // number of seconds for jumble effect +#define JUMBLE_LOOP_SPEED 35 // miliseconds between each jumble +#define REVEAL_LOOP_SPEED 50 // miliseconds between each reveal loop +#define MASK_CHAR_COUNT 218 // Total characters in maskCharTable[] array. + +// Character attribute structure, linked list. Keeps track of every +// character's attributes required for rendering and decryption effect. +struct charAttr { + char *source; + char *mask; + int width; + int is_space; + int time; + struct charAttr *next; +}; + +// Static function prototypes +static void nms_sleep(int); + +// NMS settings +static int foregroundColor = COLOR_BLUE; // Foreground color setting +static int autoDecrypt = 0; // Auto-decrypt flag +static char *returnOpts = NULL; // Return option setting +static int inputPositionX = -1; // X coordinate for input position +static int inputPositionY = -1; // Y coordinate for input position + +// Character table representing the character set know as CP437 used by +// the original IBM PC - https://en.wikipedia.org/wiki/Code_page_437 +static char *maskCharTable[] = { + "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", "~", + ".", "/", ":", ";", "<", "=", ">", "?", "[", "\\", "]", "_", "{", "}", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "\xc3\x87", "\xc3\xbc", "\xc3\xa9", "\xc3\xa2", "\xc3\xa4", "\xc3\xa0", + "\xc3\xa5", "\xc3\xa7", "\xc3\xaa", "\xc3\xab", "\xc3\xa8", "\xc3\xaf", + "\xc3\xae", "\xc3\xac", "\xc3\x84", "\xc3\x85", "\xc3\x89", "\xc3\xa6", + "\xc3\x86", "\xc3\xb4", "\xc3\xb6", "\xc3\xb2", "\xc3\xbb", "\xc3\xb9", + "\xc3\xbf", "\xc3\x96", "\xc3\x9c", "\xc2\xa2", "\xc2\xa3", "\xc2\xa5", + "\xc6\x92", "\xc3\xa1", "\xc3\xad", "\xc3\xb3", "\xc3\xba", "\xc3\xb1", + "\xc3\x91", "\xc2\xaa", "\xc2\xba", "\xc2\xbf", "\xc2\xac", "\xc2\xbd", + "\xc2\xbc", "\xc2\xa1", "\xc2\xab", "\xc2\xbb", "\xce\xb1", "\xc3\x9f", + "\xce\x93", "\xcf\x80", "\xce\xa3", "\xcf\x83", "\xc2\xb5", "\xcf\x84", + "\xce\xa6", "\xce\x98", "\xce\xa9", "\xce\xb4", "\xcf\x86", "\xce\xb5", + "\xc2\xb1", "\xc3\xb7", "\xc2\xb0", "\xc2\xb7", "\xc2\xb2", "\xc2\xb6", + "\xe2\x8c\x90", "\xe2\x82\xa7", "\xe2\x96\x91", "\xe2\x96\x92", + "\xe2\x96\x93", "\xe2\x94\x82", "\xe2\x94\xa4", "\xe2\x95\xa1", + "\xe2\x95\xa2", "\xe2\x95\x96", "\xe2\x95\x95", "\xe2\x95\xa3", + "\xe2\x95\x91", "\xe2\x95\x97", "\xe2\x95\x9d", "\xe2\x95\x9c", + "\xe2\x95\x9b", "\xe2\x94\x90", "\xe2\x94\x94", "\xe2\x94\xb4", + "\xe2\x94\xac", "\xe2\x94\x9c", "\xe2\x94\x80", "\xe2\x94\xbc", + "\xe2\x95\x9e", "\xe2\x95\x9f", "\xe2\x95\x9a", "\xe2\x95\x94", + "\xe2\x95\xa9", "\xe2\x95\xa6", "\xe2\x95\xa0", "\xe2\x95\x90", + "\xe2\x95\xac", "\xe2\x95\xa7", "\xe2\x95\xa8", "\xe2\x95\xa4", + "\xe2\x95\xa7", "\xe2\x95\x99", "\xe2\x95\x98", "\xe2\x95\x92", + "\xe2\x95\x93", "\xe2\x95\xab", "\xe2\x95\xaa", "\xe2\x94\x98", + "\xe2\x94\x8c", "\xe2\x96\x88", "\xe2\x96\x84", "\xe2\x96\x8c", + "\xe2\x96\x90", "\xe2\x96\x80", "\xe2\x88\x9e", "\xe2\x88\xa9", + "\xe2\x89\xa1", "\xe2\x89\xa5", "\xe2\x89\xa4", "\xe2\x8c\xa0", + "\xe2\x8c\xa1", "\xe2\x89\x88", "\xe2\x88\x99", "\xe2\x88\x9a", + "\xe2\x81\xbf", "\xe2\x96\xa0" +}; + +/* + * nms_exec() - This function is passed a pointer to a character string + * and displays the contents of the string in a way that mimicks the + * "decrypting text" effect in the 1992 movie Sneakers. It returns the + * last character pressed by the user. + */ +char nms_exec(char *string) { + struct charAttr *list_pointer = NULL; + struct charAttr *list_head = NULL; + struct charAttr *list_temp = NULL; + int i, revealed = 0; + int maxRows, maxCols, curRow = 0, curCol = 0; + char ret = 0; + + // Error if we have an empty string. + if (string == NULL || string[0] == '\0') { + fprintf(stderr, "Error. Empty string.\n"); + return 0; + } + + // Reassociate STDIN to the terminal is needed + if (!isatty(STDIN_FILENO) && !freopen ("/dev/tty", "r", stdin)) { + fprintf(stderr, "Error. Can't associate STDIN with terminal.\n"); + return 0; + } + + // Needed for UTF-8 support + setlocale(LC_ALL, ""); + + // Initialize terminal and settings + initscr(); + cbreak(); + noecho(); + curs_set(0); + move(0, 0); + if (has_colors()) { + start_color(); + init_pair(1, foregroundColor, COLOR_BLACK); + } + + // Get terminal window size + getmaxyx(stdscr, maxRows, maxCols); + + // Seed my random number generator with the current time + srand(time(NULL)); + + // Geting input + for (i = 0; string[i] != '\0'; ++i) { + + // Don't go beyond maxRows + if (curRow >= maxRows - 1) { + break; + } + + // Allocate memory for next list link + if (list_pointer == NULL) { + list_pointer = malloc(sizeof(struct charAttr)); + list_head = list_pointer; + } else { + list_pointer->next = malloc(sizeof(struct charAttr)); + list_pointer = list_pointer->next; + } + + // Get character's byte-length and store character. + if (mblen(&string[i], 4) > 0) { + list_pointer->source = malloc(mblen(&string[i], 4) + 1); + strncpy(list_pointer->source, &string[i], mblen(&string[i], 4)); + list_pointer->source[mblen(&string[i], 4)] = '\0'; + i += (mblen(&string[i], 4) - 1); + } else { + endwin(); + fprintf(stderr, "Unknown character encountered. Quitting.\n"); + return 0; + } + + // Set flag if we have a whitespace character + if (strlen(list_pointer->source) == 1 && isspace(list_pointer->source[0])) + list_pointer->is_space = 1; + else + list_pointer->is_space = 0; + + // Set initial mask chharacter + list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; + + // Set reveal time + list_pointer->time = rand() % 5000; + + // Set character column width + wchar_t widec[sizeof(list_pointer->source)] = {}; + mbstowcs(widec, list_pointer->source, sizeof(list_pointer->source)); + list_pointer->width = wcwidth(*widec); + + // Set next node to null + list_pointer->next = NULL; + + // Track row count + if (string[i] == '\n' || (curCol += list_pointer->width) > maxCols) { + curCol = 0; + curRow++; + } + } + + // Print mask characters with 'type effect' + for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { + + // Print mask character (or space) + if (list_pointer->is_space) { + addstr(list_pointer->source); + continue; + } + + // print mask character + addstr(list_pointer->mask); + if (list_pointer->width == 2) { + addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); + } + + // flush output and sleep + refresh(); + nms_sleep(TYPE_EFFECT_SPEED); + } + + // Flush any input up to this point + flushinp(); + + // If autoDecrypt flag is set, we sleep. Otherwise require user to + // press a key to continue. + if (autoDecrypt) + sleep(1); + else + getch(); + + // Jumble loop + for (i = 0; i < (JUMBLE_SECONDS * 1000) / JUMBLE_LOOP_SPEED; ++i) { + + // Move cursor to home position + move(0, 0); + + // Print new mask for all characters + for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { + + // Print mask character (or space) + if (list_pointer->is_space) { + addstr(list_pointer->source); + continue; + } + + // print new mask character + addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); + if (list_pointer->width == 2) { + addstr(maskCharTable[rand() % MASK_CHAR_COUNT]); + } + } + + // flush output and sleep + refresh(); + nms_sleep(JUMBLE_LOOP_SPEED); + } + + // Reveal loop + while (!revealed) { + + // Move cursor to home position + move(0, 0); + + // Set revealed flag + revealed = 1; + + for (list_pointer = list_head; list_pointer != NULL; list_pointer = list_pointer->next) { + + // Print mask character (or space) + if (list_pointer->is_space) { + addstr(list_pointer->source); + continue; + } + + // If we still have time before the char is revealed, display the mask + if (list_pointer->time > 0) { + + // Change the mask randomly + if (list_pointer->time < 500) { + if (rand() % 3 == 0) { + list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; + } + } else { + if (rand() % 10 == 0) { + list_pointer->mask = maskCharTable[rand() % MASK_CHAR_COUNT]; + } + } + + // Print mask + addstr(list_pointer->mask); + + // Decrement reveal time + list_pointer->time -= REVEAL_LOOP_SPEED; + + // Unset revealed flag + revealed = 0; + } else { + + // Set bold and foreground color for character reveal + attron(A_BOLD); + if (has_colors()) + attron(COLOR_PAIR(1)); + + // print source character + addstr(list_pointer->source); + + // Unset foreground color + if (has_colors()) + attroff(COLOR_PAIR(1)); + attroff(A_BOLD); + } + } + + // flush output and sleep + refresh(); + nms_sleep(REVEAL_LOOP_SPEED); + } + + // Flush any input up to this point + flushinp(); + + // Check if user must select from a set of options + if (returnOpts != NULL && strlen(returnOpts) > 0) { + + // Position cursor if necessary + if (inputPositionY >= 0 && inputPositionX >= 0) { + move(inputPositionY, inputPositionX); + } + + // Turn on cursor + curs_set(1); + refresh(); + + // Get and validate user selection + while (strchr(returnOpts, ret = getch()) == NULL) { + beep(); + } + + } + + // User must press a key to continue when clearSrc is set + // without returnOpts + else { + getch(); + } + + // End curses mode + endwin(); + + // Freeing the list. + list_pointer = list_head; + while (list_pointer != NULL) { + list_temp = list_pointer; + list_pointer = list_pointer->next; + free(list_temp->source); + free(list_temp); + } + + return ret; +} + +/* + * nms_set_foreground_color() sets the foreground color of the unencrypted + * characters as they are revealed to the color indicated by the 'color' + * argument. Valid arguments are "white", "yellow", "magenta", "blue", + * "green", "red", and "cyan". This function will default to blue if + * passed an invalid color. No value is returned. + */ +void nms_set_foreground_color(char *color) { + + if(strcmp("white", color) == 0) + foregroundColor = COLOR_WHITE; + else if(strcmp("yellow", color) == 0) + foregroundColor = COLOR_YELLOW; + else if(strcmp("black", color) == 0) + foregroundColor = COLOR_BLACK; + else if(strcmp("magenta", color) == 0) + foregroundColor = COLOR_MAGENTA; + else if(strcmp("blue", color) == 0) + foregroundColor = COLOR_BLUE; + else if(strcmp("green", color) == 0) + foregroundColor = COLOR_GREEN; + else if(strcmp("red", color) == 0) + foregroundColor = COLOR_RED; + else if(strcmp("cyan", color) == 0) + foregroundColor = COLOR_CYAN; + else + foregroundColor = COLOR_BLUE; +} + +/* + * nms_set_auto_decrypt() sets the autoDecrypt flag according to the + * true/false value of the 'setting' argument. + */ +void nms_set_auto_decrypt(int setting) { + if (setting) + autoDecrypt = 1; + else + autoDecrypt = 0; +} + +/* + * nms_set_clear_scr() sets the clearScr flag according to the + * true/false value of the 'setting' argument. NOTE: FOR NCURSES THIS + * IS NOT USED. + */ +void nms_set_clear_scr(int setting) { + (void) setting; + return; +} + +/* + * nms_set_input_position() sets the desired coordinate of the cursor in + * the terminal when accepting user input after nms_exec() reveals the + * unencrypted characters. + */ +void nms_set_input_position(int x, int y) { + if (x >= 0 && y >= 0) { + inputPositionX = x; + inputPositionY = y; + } +} + +/* + * nms_sleep() sleeps for the number of miliseconds indicated by 't'. + */ +static void nms_sleep(int t) { + struct timespec ts; + + ts.tv_sec = t / 1000; + ts.tv_nsec = (t % 1000) * 1000000; + + nanosleep(&ts, NULL); +}