Changed encryption to use OpenSSL crypto message digest
[wordblah.git] / wordblox.h
1 #ifndef __WORDBLOX_H
2 #define __WORDBLOX_H
3 #define _XOPEN_SOURCE
4 #include <unistd.h>
5 #include <stdbool.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <gd.h>
9 #include <gdfontmb.h>
10 #include <gdfontg.h>
11 #include <zlib.h>
12 #include <openssl/conf.h>
13 #include <openssl/evp.h>
14 #include <openssl/err.h>
15 #include "constantstrings.h"
16
17 #define MAX_PUZZLE_SIZE 25
18 #define MAX_CLUE_LENGTH 150
19 #define GRID_PIXELS 37
20
21 /* Enum to define terminal colours */
22 enum COLOR {
23 BLACK = 0,
24 RED= 1,
25 GREEN=2,
26 YELLOW=3,
27 BLUE=4,
28 MAGENTA=5,
29 CYAN=6,
30 WHITE=7
31 };
32
33 enum ATTR {
34 NORMAL = 23,
35 BOLD=1
36 };
37
38 enum ORIENTATION {
39 ACROSS=1,
40 DOWN=2
41 };
42
43 /* for use with the player */
44 enum DIRECTION {
45 DIR_FORWARD = 1,
46 DIR_BACK = -1
47 };
48
49 typedef char String[MAX_CLUE_LENGTH];
50
51 /* The main puzzle struct type */
52 typedef struct {
53 char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
54 int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
55 int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
56 String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
57 String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
58 int grid_size;
59 bool grid_frozen;
60 char hashed_password[256];
61 char salt[256];
62 } Puzzle;
63
64 /* The player data struct type - for the player app */
65 typedef struct {
66 Puzzle puzzle;
67 char filename[65535];
68 bool is_loaded;
69 char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
70 int cur_row;
71 int cur_col;
72 } MainPlayerData;
73
74 /* compute the hash of a password */
75 void digest_message(const unsigned char *message,
76 size_t message_len, unsigned char **digest, unsigned int *digest_len)
77 {
78 EVP_MD_CTX *mdctx;
79
80 if((mdctx = EVP_MD_CTX_new()) == NULL)
81 goto err;
82
83 if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
84 goto err;
85
86 if(1 != EVP_DigestUpdate(mdctx, message, message_len))
87 goto err;
88
89 if((*digest = (unsigned char *)
90 OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
91 goto err;
92
93 if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
94 goto err;
95
96 EVP_MD_CTX_free(mdctx);
97 return;
98 err:
99 EVP_MD_CTX_free(mdctx);
100 ERR_print_errors_fp(stderr);
101 exit (2);
102 }
103
104 /* get a number from the user */
105 int get_num ()
106 {
107 char s[5];
108 fgets (s, 5, stdin);
109 int n = atoi (s);
110 return n;
111 }
112
113 /* verify password */
114 bool verify_password (Puzzle *p, const char* password)
115 {
116 /* no password set */
117 if (strcmp (p->hashed_password, "\0") == 0)
118 return true;
119
120 /* hash the user input password and compare it with the stored password */
121 unsigned char* hashed_password;
122 unsigned int len;
123 digest_message ((const unsigned char *)password, strlen(password),
124 &hashed_password, &len);
125
126 if (strcmp (p->hashed_password, (const char*) hashed_password) == 0)
127 {
128 OPENSSL_free (hashed_password);
129 return true;
130 }
131 OPENSSL_free (hashed_password);
132 return false;
133 }
134
135 /* Set or reset password for puzzle */
136 void set_puzzle_password (Puzzle *p, const char *password)
137 {
138 /* if it is a null string, reset the password */
139 if (strcmp (password, "\0") == 0)
140 {
141 strcpy (p->hashed_password, "\0");
142 strcpy (p->salt, "\0");
143 }
144 else
145 {
146
147 unsigned char* hashedpwd;
148 unsigned int len;
149 digest_message ((const unsigned char *)password, strlen(password),
150 &hashedpwd, &len);
151 strcpy (p->hashed_password, (const char *)hashedpwd);
152 strcpy (p->salt, "\0");
153 OPENSSL_free (hashedpwd);
154 }
155 }
156
157 /* Output the clues to text file */
158 void export_clues (Puzzle *p, const char *filename)
159 {
160 FILE *outfile = fopen (filename, "w");
161 if (outfile == NULL)
162 {
163 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
164 exit (1);
165 }
166 /* first the across clues */
167 fprintf (outfile, "ACROSS CLUES\n");
168 for (int i = 0; i < p->grid_size; i ++)
169 {
170 for (int j = 0; j < p->grid_size; j ++)
171 {
172 if (p->start_across_word[i][j] != -1)
173 fprintf (outfile, "%d - %s\n", p->start_across_word[i][j],
174 p->clue_across[i][j]);
175 }
176 }
177 /* now the down clues */
178 fprintf (outfile, "DOWN CLUES\n");
179 for (int i = 0; i < p->grid_size; i ++)
180 {
181 for (int j = 0; j < p->grid_size; j ++)
182 {
183 if (p->start_down_word[i][j] != -1)
184 fprintf (outfile, "%d - %s\n", p->start_down_word[i][j],
185 p->clue_down[i][j]);
186 }
187 }
188 fclose (outfile);
189 }
190
191 /* Output the grid to image - if answerkey is true export filled grid */
192 void export_grid_image (Puzzle *p, const char *filename, bool answerkey)
193 {
194 int img_size = p->grid_size * GRID_PIXELS;
195 FILE * outfile = fopen (filename, "wb");
196 if (outfile == NULL)
197 {
198 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
199 exit (1);
200 }
201
202 gdImagePtr img = gdImageCreate (img_size, img_size);
203 gdImageColorAllocate (img, 255,255,255);
204 int black = gdImageColorAllocate (img, 0, 0, 0);
205 int blue = gdImageColorAllocate (img, 0, 0, 216);
206 gdFontPtr sm_fnt = gdFontGetMediumBold ();
207 gdFontPtr lg_fnt = gdFontGetGiant ();
208
209 for (int i = 0; i < p->grid_size; i ++)
210 {
211 for (int j = 0; j < p->grid_size; j++)
212 {
213 /* if it is a block, draw the black square */
214 if (p->chars[i][j] == '#')
215 gdImageFilledRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
216 j*GRID_PIXELS+GRID_PIXELS,
217 i*GRID_PIXELS+GRID_PIXELS,black);
218 else
219 {
220 /* draw a regular square */
221 gdImageRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
222 j*GRID_PIXELS+GRID_PIXELS,
223 i*GRID_PIXELS+GRID_PIXELS, black);
224
225 /* print the numers, if it is either start across word or
226 a down word */
227 if (p->start_across_word[i][j] != -1 ||
228 p->start_down_word[i][j] != -1)
229 {
230 if (p->start_across_word[i][j] != -1)
231 {
232 char str[5];
233 sprintf (str, "%d", p->start_across_word[i][j]);
234 gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
235 i*GRID_PIXELS+2,
236 (unsigned char *)str, blue);
237 }
238 else
239 {
240 char str[5];
241 sprintf (str, "%d", p->start_down_word[i][j]);
242 gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
243 i*GRID_PIXELS+2,
244 (unsigned char *)str, blue);
245 }
246 }
247 /* if answerkey is true, draw the character in the cell */
248 if (answerkey)
249 {
250 gdImageChar (img, lg_fnt, j*GRID_PIXELS+15,
251 i*GRID_PIXELS+10, p->chars[i][j], black);
252 }
253 }
254 }
255 }
256
257 gdImagePng (img, outfile);
258 gdImageDestroy (img);
259 fclose (outfile);
260 }
261
262 /* Set the terminal colour */
263 void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) {
264 printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at);
265 }
266
267 /* Reset the terminal colour */
268 void reset_color () {
269 printf ("\x1B[0m");
270 }
271
272 /* check if previous row is blank or not */
273 bool prev_row_blank (Puzzle *p, int r, int c)
274 {
275 if (r == 0) return true;
276 if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true;
277 return false;
278 }
279 /* check if next row is blank or not */
280 bool next_row_blank (Puzzle *p, int r, int c)
281 {
282 if (r == p->grid_size - 1) return true;
283 if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true;
284 return false;
285 }
286 /* check if previous col is blank or not */
287 bool prev_col_blank (Puzzle *p, int r, int c)
288 {
289 if (c == 0) return true;
290 if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true;
291 return false;
292 }
293 /* check if the next col is blank or not */
294 bool next_col_blank (Puzzle *p, int r, int c)
295 {
296 if (c == p->grid_size -1) return true;
297 if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true;
298 return false;
299 }
300
301 /* unfreeze the grid - make editing possible to change words */
302 void unfreeze_puzzle (Puzzle *p)
303 {
304 for (int i = 0; i < p->grid_size; i ++)
305 {
306 for (int j = 0; j < p->grid_size; j ++)
307 {
308 if (p->chars[i][j] == '#')
309 p->chars[i][j] = ' ';
310
311 p->start_across_word[i][j] = -1;
312 p->start_down_word[i][j] = -1;
313 }
314 }
315 p->grid_frozen = false;
316 }
317
318 /* freeze the grid - make editing impossible because it finalizes the
319 across and down words in the grid */
320 void freeze_puzzle (Puzzle *p)
321 {
322 int word_num = 1;
323 bool across_word_start, down_word_start;
324 for (int i = 0; i < p->grid_size; i ++)
325 {
326 for (int j = 0; j < p->grid_size; j++)
327 {
328 across_word_start = false;
329 down_word_start = false;
330 /* if it is a blank cell - cover it with a block */
331 if (p->chars[i][j] == ' ' || p->chars[i][j] == '#')
332 p->chars[i][j] = '#';
333 /* it is not a blank cell - check all possibilities */
334 else
335 {
336 bool prev_row = prev_row_blank (p, i, j);
337 bool next_row = next_row_blank (p, i, j);
338 bool prev_col = prev_col_blank (p, i, j);
339 bool next_col = next_col_blank (p, i, j);
340 if (prev_row && ! next_row)
341 down_word_start = true;
342 if (prev_col && ! next_col)
343 across_word_start = true;
344 }
345
346 if (across_word_start == true)
347 p->start_across_word[i][j] = word_num;
348 else
349 p->start_across_word[i][j] = -1;
350 if (down_word_start == true)
351 p->start_down_word[i][j] = word_num;
352 else
353 p->start_down_word[i][j] = -1;
354 if (across_word_start == true || down_word_start == true)
355 word_num ++;
356 }
357 }
358 p->grid_frozen = true;
359 }
360
361 /* reset the entire grid */
362 void init_puzzle (Puzzle *p, int grid_size)
363 {
364 p->grid_size = grid_size;
365 p->grid_frozen = false;
366 for (int i = 0; i < p->grid_size; i ++)
367 {
368 for (int j = 0; j < p->grid_size; j ++)
369 {
370 p->chars[i][j] = ' ';
371 p->start_across_word[i][j] = -1;
372 p->start_down_word[i][j] = -1;
373 strcpy (p->clue_across[i][j], "");
374 strcpy (p->clue_down[i][j], "");
375 }
376 }
377 strcpy (p->hashed_password, "\0");
378 strcpy (p->salt, "\0");
379
380 }
381
382 /* save the puzzle to a file */
383 void save_puzzle (Puzzle *puzzle, const char* file) {
384 FILE *outfile;
385 /* First output the uncompressed contents to a temp file */
386 outfile = tmpfile ();
387 if (outfile == NULL)
388 {
389 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
390 exit (1);
391 }
392 /* grid size is the first field */
393 fprintf (outfile, "%d\n", puzzle->grid_size);
394 /* whether grid is frozen or not */
395 fprintf (outfile, "%d\n", puzzle->grid_frozen);
396 /* the hashed password */
397 fprintf (outfile, "%s\n", puzzle->hashed_password);
398 /* the salt */
399 fprintf (outfile, "%s\n", puzzle->salt);
400
401 /* First output the grid characters columns/rows */
402 for (int i = 0; i < puzzle->grid_size; i ++)
403 {
404 for (int j = 0; j < puzzle->grid_size; j ++)
405 fprintf (outfile, "%c", puzzle->chars[i][j]);
406 fprintf (outfile, "\n");
407 }
408
409 /* Next output the start across/down numbers */
410 for (int i = 0; i < puzzle->grid_size; i ++)
411 {
412 for (int j = 0; j < puzzle->grid_size; j++)
413 {
414 fprintf (outfile, "%d ", puzzle->start_across_word[i][j]);
415 fprintf (outfile, "%d ", puzzle->start_down_word[i][j]);
416 }
417 fprintf (outfile, "\n");
418 }
419
420 /* Output the across clues */
421 fprintf (outfile, "ACROSS\n");
422 /* Search the grid for across words */
423 for (int i = 0; i < puzzle->grid_size; i ++)
424 {
425 for (int j = 0; j < puzzle->grid_size; j++)
426 {
427 /* if it is an across word, then put the word index followed by
428 tab character (as separator) and the clue */
429 if (puzzle->start_across_word[i][j] != -1)
430 fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j],
431 puzzle->clue_across[i][j]);
432 }
433 }
434
435 /* Output the down clues */
436 fprintf (outfile, "DOWN\n");
437 /* Search the grid for down words */
438 for (int i = 0; i < puzzle->grid_size; i ++)
439 {
440 for (int j = 0; j < puzzle->grid_size; j++)
441 {
442 /* same as across word, put the word index followed by the tab
443 character and then the clue */
444 if (puzzle->start_down_word[i][j] != -1)
445 fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j],
446 puzzle->clue_down[i][j]);
447 }
448 }
449
450 /* Flush the buffer and rewind to beginning - to read and save into
451 gzip compressed file */
452 fflush (outfile);
453 fseek (outfile, 0, 0);
454
455 /* now compress the file and save it to destination file */
456 gzFile outdestfile = gzopen (file, "wb");
457 if (outdestfile == NULL)
458 {
459 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
460 fclose (outfile);
461 exit (1);
462 }
463 char buf[128];
464 int num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
465 while (num > 0)
466 {
467 int res = gzwrite (outdestfile, buf, num*sizeof(char) );
468 if (res == 0)
469 {
470 fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED);
471 fclose (outfile);
472 exit (1);
473 }
474 num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
475 }
476 gzclose (outdestfile);
477 fclose (outfile);
478
479 }
480
481 /* read the puzzle from a file */
482 Puzzle load_puzzle (const char* file) {
483 /* First open the GZip file */
484 gzFile insourcefile = gzopen (file, "rb");
485 if (insourcefile == NULL)
486 {
487 fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED);
488 exit (1);
489 }
490 /* Open a temporary file to uncompress the contents */
491 FILE *infile = tmpfile ();
492 if (infile == NULL)
493 {
494 fprintf (stderr, "%s\n", ERROR_READING_FILE);
495 exit (1);
496 }
497 /* Put the uncompressed content to the temp file */
498 char buf[128];
499 int num = 0;
500 num = gzread (insourcefile, buf, 128);
501 while (num > 0)
502 {
503 int res = fwrite (buf, 1, num, infile);
504 if (res == 0)
505 {
506 fprintf (stderr, "%s\n", ERROR_READING_FILE);
507 fclose (infile);
508 gzclose (insourcefile);
509 exit (1);
510 }
511 num = gzread (insourcefile, buf, 128);
512 }
513 /* Close the gzip file */
514 gzclose (insourcefile);
515 /* Flush the temp file buffer and rewind to beginning */
516 fflush (infile);
517 fseek (infile, 0, 0);
518
519 /* Read the temporary file contents to the structure Puzzle */
520 Puzzle p;
521 char line[MAX_CLUE_LENGTH+10];
522 fgets (line, MAX_CLUE_LENGTH + 10, infile);
523 p.grid_size = atoi (line);
524 fgets (line, MAX_CLUE_LENGTH + 10, infile);
525 p.grid_frozen = atoi (line) == 0 ? false : true ;
526 fgets (line, MAX_CLUE_LENGTH + 10, infile);
527 if (strlen (line) != 1)
528 strcpy (p.hashed_password, strtok (line, "\n"));
529 else
530 strcpy (p.hashed_password, "\0");
531 fgets (line, MAX_CLUE_LENGTH + 10, infile);
532 if (strlen (line) != 1)
533 strcpy (p.salt, strtok (line, "\n"));
534 else
535 strcpy (p.salt, "\0");
536
537 /* read each character of the grid */
538 for (int i = 0; i < p.grid_size; i ++ )
539 {
540 fgets (line, MAX_CLUE_LENGTH + 10, infile);
541 for (int j = 0; j < p.grid_size; j ++)
542 p.chars[i][j] = line[j];
543 }
544 /* read the word numbers */
545 for (int i = 0; i < p.grid_size; i ++)
546 {
547 fgets (line, MAX_CLUE_LENGTH + 10, infile);
548 char *token = strtok (line, " ");
549 for (int j = 0; j < p.grid_size; j ++)
550 {
551 if (token != NULL)
552 p.start_across_word[i][j] = atoi (token);
553 token = strtok (NULL, " ");
554 if (token != NULL)
555 p.start_down_word[i][j] = atoi (token);
556 token = strtok (NULL, " ");
557 }
558 }
559 /* read the clues */
560 fgets (line, MAX_CLUE_LENGTH + 10, infile);
561
562 /* across clues */
563 char clues[100][MAX_CLUE_LENGTH];
564 int word_num[100];
565 int c = 0;
566 /* first read the across clues from file */
567 while (1)
568 {
569 fgets (line, MAX_CLUE_LENGTH + 10, infile);
570 /* if reached the end of across clues */
571 if (strcmp (line, "DOWN\n") == 0)
572 break;
573 word_num[c] = atoi (strtok (line, "\t"));
574 char *cl = strtok (NULL, "\n");
575 if (cl != NULL)
576 strcpy (clues[c], cl);
577 else
578 strcpy (clues[c], "\0");
579 c++;
580 }
581 /* set the clue to the correct cell in grid */
582 for (int i = 0; i < p.grid_size; i ++)
583 {
584 for (int j = 0; j < p.grid_size; j ++)
585 {
586 for (int r = 0; r < c; r ++)
587 if (p.start_across_word[i][j] == word_num[r])
588 strcpy (p.clue_across[i][j], clues[r]);
589 }
590 }
591
592 /* down clues */
593 c = 0;
594 while (fgets (line, MAX_CLUE_LENGTH + 10, infile))
595 {
596 word_num[c] = atoi (strtok (line, "\t"));
597 char* cl = strtok (NULL, "\n");
598 if (cl != NULL)
599 strcpy (clues[c], cl);
600 else
601 strcpy (clues[c], "\0");
602 c++;
603 }
604 for (int i = 0; i < p.grid_size; i ++)
605 {
606 for (int j = 0; j < p.grid_size; j ++)
607 {
608 for (int r = 0; r < c; r ++)
609 if (p.start_down_word[i][j] == word_num[r])
610 strcpy (p.clue_down[i][j], clues[r]);
611 }
612 }
613
614 fclose (infile);
615 return p;
616 }
617
618 /* display the puzzle */
619 void print_puzzle (Puzzle *p)
620 {
621 printf ("\n");
622 set_color (WHITE, CYAN, NORMAL);
623 printf (" ");
624 for (int i = 0; i < p->grid_size; i ++)
625 printf ("%3d", i);
626 reset_color ();
627 printf("\n");
628 for (int i = 0; i < p->grid_size; i ++)
629 {
630 set_color (WHITE, CYAN, NORMAL);
631 printf ("%3d ", i);
632 for (int j = 0; j < p->grid_size; j ++)
633 {
634 if (p->chars[i][j] == '#') {
635 set_color (WHITE, BLACK, NORMAL);
636 printf (" ");
637 }
638 else
639 {
640 if (p->start_across_word[i][j] != -1 ||
641 p->start_down_word[i][j] != -1)
642 {
643 set_color (BLUE, WHITE, NORMAL);
644 if (p->start_across_word[i][j] != -1)
645 printf ("%-2d", p->start_across_word[i][j]);
646 else
647 printf ("%-2d", p->start_down_word[i][j]);
648 }
649 else
650 {
651 set_color (BLACK, WHITE,NORMAL);
652 printf (" ");
653 }
654
655 set_color (BLACK, WHITE, BOLD);
656 printf ("%c", p->chars[i][j]);
657 }
658 reset_color ();
659 }
660 printf ("\n");
661 }
662 /* print the clues if set */
663 if (p->grid_frozen == true)
664 {
665 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
666 for (int i = 0; i < p->grid_size; i ++)
667 {
668 for (int j = 0; j < p->grid_size; j ++)
669 {
670 if (p->start_across_word[i][j] != -1)
671 {
672 printf ("%d - %s; ", p->start_across_word[i][j],
673 p->clue_across[i][j]);
674 }
675 }
676 }
677 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
678 for (int i = 0; i < p->grid_size; i ++)
679 {
680 for (int j = 0; j < p->grid_size; j ++)
681 {
682 if (p->start_down_word[i][j] != -1)
683 {
684 printf ("%d - %s; ", p->start_down_word[i][j],
685 p->clue_down[i][j]);
686 }
687 }
688 }
689 printf ("\n");
690 }
691 }
692
693 /* function to check if a word is valid or not */
694 char* is_valid_word (char *word)
695 {
696 if (word == NULL || strlen(word) == 0)
697 return NULL;
698 for (int i = 0; i < strlen (word) - 1; i ++)
699 if (! isalpha (word[i]))
700 return NULL;
701
702 return strtok (word, "\n");
703 }
704
705
706 /* function to set a clue for an across word */
707 bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
708 {
709 for (int i = 0; i < p->grid_size; i ++)
710 {
711 for (int j = 0; j < p->grid_size; j ++)
712 {
713 if (order == ACROSS)
714 {
715 if (p->start_across_word[i][j] == index)
716 {
717 strcpy (p->clue_across[i][j], clue);
718 return true;
719 }
720 }
721 else if (order == DOWN)
722 {
723 if (p->start_down_word[i][j] == index)
724 {
725 strcpy (p->clue_down[i][j], clue);
726 return true;
727 }
728 }
729 }
730 }
731 return false;
732 }
733
734 /* function to print a menu */
735 void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
736 char **items, int num_items, int padding)
737 {
738 /* clear screen */
739 printf ("\e[1;1H\e[2J");
740 set_color (fg, bg, NORMAL);
741 printf ("\u2554");
742 for (int i = 0; i < padding; i ++)
743 printf ("\u2550");
744 printf ("\u2557");
745 reset_color (); printf ("\n");
746 set_color (fg, bg, NORMAL);
747 printf ("\u2551");
748 set_color (fg, bg, BOLD);
749 printf ("%-*s", padding, title);
750 reset_color ();
751 set_color (fg, bg, NORMAL);
752 printf ("\u2551");
753 reset_color (); printf ("\n");
754 set_color (fg, bg, NORMAL);
755 printf ("\u2560");
756 for (int i = 0; i < padding; i ++)
757 printf ("\u2550");
758 printf ("\u2563");
759 reset_color (); printf ("\n");
760 for (int i = 0; i < num_items; i ++)
761 {
762 set_color (fg, bg, NORMAL);
763 printf ("\u2551%-*s\u2551", padding, items[i]);
764 reset_color (); printf ("\n");
765 }
766 set_color (fg, bg, NORMAL);
767 printf ("\u255A");
768 for (int i = 0; i < padding; i ++)
769 printf ("\u2550");
770 printf ("\u255D");
771 reset_color (); printf ("\n");
772 }
773
774 /* reset the player data, from the new file */
775 void reset_player_data (MainPlayerData *app_data, const char *filename)
776 {
777 app_data->puzzle = load_puzzle (filename);
778
779 app_data->is_loaded = app_data->puzzle.grid_frozen;
780 app_data->cur_col = -1;
781 app_data->cur_row = -1;
782 strcpy (app_data->filename, filename);
783 /* reset the answer keys */
784 for (int i = 0; i < app_data->puzzle.grid_size; i ++)
785 for (int j = 0; j < app_data->puzzle.grid_size; j ++)
786 app_data->char_ans[i][j] = ' ';
787
788 }
789
790 /* in the player app, move the current selection index left or right */
791 void move_current_col (MainPlayerData *app_data, enum DIRECTION dir)
792 {
793 int r = app_data->cur_row;
794 int c = app_data->cur_col;
795 if (dir == DIR_FORWARD)
796 {
797 c ++;
798 while (c < app_data->puzzle.grid_size)
799 {
800 if (app_data->puzzle.chars[r][c] == '#')
801 c ++;
802 else
803 break;
804 }
805 if (c < app_data->puzzle.grid_size)
806 app_data->cur_col = c;
807 }
808 else
809 {
810 c --;
811 while (c >= 0)
812 {
813 if (app_data->puzzle.chars[r][c] == '#')
814 c --;
815 else
816 break;
817 }
818 if (c >= 0)
819 app_data->cur_col = c;
820 }
821 }
822
823 /* in the player app move the current selection index up or down */
824 void move_current_row (MainPlayerData *app_data, enum DIRECTION dir)
825 {
826 int r = app_data->cur_row;
827 int c = app_data->cur_col;
828 if (dir == DIR_FORWARD)
829 {
830 r ++;
831 while (r < app_data->puzzle.grid_size)
832 {
833 if (app_data->puzzle.chars[r][c] == '#')
834 r ++;
835 else
836 break;
837 }
838 if (r < app_data->puzzle.grid_size)
839 app_data->cur_row = r;
840 }
841 else
842 {
843 r --;
844 while (r >= 0)
845 {
846 if (app_data->puzzle.chars[r][c] == '#')
847 r --;
848 else
849 break;
850 }
851 if (r >= 0)
852 app_data->cur_row = r;
853 }
854 }
855
856 #endif