gol

Implementation of Conway's Game of Life writen in C
git clone git://git.dimitrijedobrota.com/gol.git
Log | Files | Refs | README

file.c (8163B)


      1 /**
      2  * @file file.c
      3  * @author Dimitrije Dobrota
      4  * @date 16 June 2022
      5  * @brief This file contains functions for handling save files
      6  *
      7  * This file aims to provide a simple interface for interacting with the
      8  * file system on Linux and Windows systems. After proper game directory has
      9  * been selected, functions implemented here will read the list of files, filter
     10  * them based on the extension, create new files for storing a whole game or
     11  * just a pattern.
     12  */
     13 
     14 #include <dirent.h>
     15 #include <limits.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 #include <sys/stat.h>
     19 #include <sys/types.h>
     20 #include <unistd.h>
     21 
     22 #include "display.h"
     23 #include "game.h"
     24 #include "logic.h"
     25 #include "utils.h"
     26 
     27 #ifdef _WIN32
     28 #define SETTINGS_DIR  "C:\\GoL" // without trailing '\'
     29 #define MAKE_DIR(dir) (mkdir(dir))
     30 #else
     31 #define SETTINGS_DIR  "GoL"
     32 #define MAKE_DIR(dir) (mkdir(dir, 0777))
     33 #endif // _WIN32
     34 
     35 /**
     36  * @brief Check if a directory at specific path exists
     37  */
     38 int DirectoryExists(const char *path) {
     39   struct stat stats;
     40   stat(path, &stats);
     41 
     42   return S_ISDIR(stats.st_mode);
     43 }
     44 
     45 /**
     46  * @brief Try to change the directory to SETTINGS_DIR, if it doesn't exist
     47  * create it. Return 0 on failure.
     48  */
     49 void file_setup(void) {
     50   char *dir;
     51   MEM_CHECK(dir = malloc(PATH_MAX * sizeof(char)));
     52 
     53 #ifdef _WIN32
     54   strcpy(dir, SETTINGS_DIR);
     55 #else
     56   const char *homedir = getenv("HOME");
     57   sprintf(dir, "%s/%s", homedir, SETTINGS_DIR);
     58 #endif
     59 
     60   if (!DirectoryExists(dir)) {
     61     printf("Directory %s does not exists; Trying to create it...\n", dir);
     62     if (MAKE_DIR(dir) != 0)
     63       err("Cannot create the directory %s", dir);
     64   }
     65 
     66   if (chdir(dir) != 0) {
     67     printf("Cannot change the directory\n");
     68     if (MAKE_DIR(dir) != 0)
     69       err("Cannot change directory to %s", dir);
     70   }
     71 
     72   free(dir);
     73 }
     74 
     75 typedef struct file_T *file_T;
     76 
     77 /**
     78  * @brief A node in a linked list of file names
     79  */
     80 struct file_T {
     81   file_T next; ///< pointer to the next node
     82   char  *name; ///< name of a file
     83 };
     84 
     85 static file_T loaded_files;
     86 
     87 /**
     88  * @brief create new file_T
     89  *
     90  * Allocate the memory for a new file_T. The name field will be set to NULL if
     91  * NULL is provided as name, otherwise it will be set to a DUPLICATE of a
     92  * string provided.
     93  */
     94 file_T file_new(char *name) {
     95   file_T f;
     96   MEM_CHECK(f = calloc(1, sizeof(*f)));
     97 
     98   if (name != NULL) {
     99     MEM_CHECK(f->name = malloc((strlen(name) + 1) * sizeof(char)));
    100     strcpy(f->name, name);
    101   }
    102 
    103   return f;
    104 }
    105 
    106 /**
    107  * @brief Free the linked list of file_T
    108  */
    109 void file_free(file_T self) {
    110   if (self == NULL)
    111     return;
    112 
    113   file_free(self->next);
    114   free(self->name);
    115   free(self);
    116 }
    117 
    118 /**
    119  * @brief Add file of a specific name to the begin of a linked list
    120  *
    121  * First item of the list will be skipped and unchanged.
    122  */
    123 file_T file_add(file_T self, char *name) {
    124   file_T new = file_new(name);
    125   new->next = self->next;
    126   self->next = new;
    127   return new;
    128 }
    129 
    130 /**
    131  * @brief Sort linked list in a lexicographical order
    132  */
    133 void file_sort(file_T self) {
    134   for (file_T p = self->next; p; p = p->next)
    135     for (file_T t = p->next; t; t = t->next)
    136       if (strcmp(p->name, t->name) > 0) {
    137         char *tmp = p->name;
    138         p->name = t->name;
    139         t->name = tmp;
    140       }
    141 }
    142 
    143 /**
    144  * @brief Return a new linked list of files contained in the current directory
    145  */
    146 file_T file_fromDirectory(void) {
    147   file_T base = file_new(NULL);
    148 
    149   struct dirent *de;
    150   DIR           *dr = opendir(".");
    151   if (dr == NULL)
    152     return NULL;
    153 
    154   while ((de = readdir(dr)) != NULL)
    155     file_add(base, de->d_name);
    156 
    157   file_sort(base);
    158   file_T n = base->next;
    159 
    160   free(base);
    161   free(dr);
    162 
    163   return n;
    164 }
    165 
    166 /**
    167  * @brief Check if file is in the linked list, return NULL if it's not
    168  */
    169 file_T file_find(file_T self, char *name) {
    170   for (; self != NULL; self = self->next) {
    171     if (!strcmp(self->name, name))
    172       return self;
    173   }
    174   return NULL;
    175 }
    176 
    177 /**
    178  * @brief Return array of the filenames that have the specific extension
    179  *
    180  * Memory for the buffer is allocated automatically and should be freed with
    181  * free() after it's no longer needed.
    182  */
    183 int file_select_extension(char *ext, char ***buffer) {
    184   int    maxsize = 4;
    185   int    size = 0;
    186   char **tmp = NULL;
    187   file_T current = loaded_files;
    188 
    189   MEM_CHECK(*buffer = malloc(maxsize * sizeof(char *)));
    190   for (; current != NULL; current = current->next) {
    191     char *dot = strrchr(current->name, '.');
    192     if (dot == NULL)
    193       continue;
    194     if (!strcmp(dot + 1, ext)) {
    195       *dot = '\0';
    196       (*buffer)[size++] = current->name;
    197       if (size == maxsize) {
    198         maxsize *= 2;
    199         MEM_CHECK(tmp = realloc(*buffer, maxsize * sizeof(char *)));
    200         *buffer = tmp;
    201       }
    202     }
    203   }
    204   if (size) {
    205     MEM_CHECK(tmp = realloc(*buffer, size * sizeof(char *)));
    206     *buffer = tmp;
    207   }
    208 
    209   return size;
    210 }
    211 
    212 /**
    213  * @brief Fill loaded_files linked list with files in the current directory,
    214  * freeing any previous lists
    215  */
    216 void load_files(void) {
    217   if (loaded_files)
    218     file_free(loaded_files);
    219   loaded_files = file_fromDirectory();
    220 }
    221 
    222 /**
    223  * @brief Free loaded_files linked list
    224  */
    225 void free_files(void) { file_free(loaded_files); }
    226 
    227 // from logic.c
    228 extern Cell **save_cells;   ///< List of Cells to be saved in a pattern
    229 extern int    save_cells_s; ///< Size of save_cells
    230 extern int    pos_y;        ///< Real cursor y coordinate
    231 extern int    pos_x;        ///< Real cursor x coordinate
    232 extern int    evolve_index; ///< index of the current game mode
    233 
    234 // form game.c
    235 extern int height; ///< height of the current game
    236 extern int width;  ///< width of the current game
    237 
    238 /**
    239  * @brief Load a pattern to the current cursor position from a file with name
    240  * and extension .part
    241  */
    242 void file_load_pattern(char *name, int index) {
    243   FILE *f;
    244   char *fname;
    245   int   min_y = INT_MAX, min_x = INT_MAX, max_y = -1, max_x = -1;
    246   int   row, col, val;
    247 
    248   MEM_CHECK(fname = malloc((strlen(name) + 5) * sizeof(char)));
    249   sprintf(fname, "%s.part", name);
    250 
    251   FILE_CHECK(f = fopen(fname, "r"));
    252 
    253   while (fscanf(f, "%d %d %d", &row, &col, &val) != EOF) {
    254     min_y = MIN(min_y, row);
    255     min_x = MIN(min_x, col);
    256     max_y = MAX(max_y, row);
    257     max_x = MAX(max_x, col);
    258   }
    259 
    260   for (int i = min_y; i <= max_y; i++)
    261     for (int j = min_x; j <= max_x; j++)
    262       setAt(i + pos_y, j + pos_x, 0);
    263 
    264   rewind(f);
    265   while (fscanf(f, "%d %d %d", &row, &col, &val) != EOF)
    266     if (height != 0 && width != 0)
    267       setAt(WCLAMP(pos_y + row, height), WCLAMP(pos_x + col, width), val);
    268     else
    269       setAt(pos_y + row, pos_x + col, val);
    270 }
    271 
    272 /**
    273  * @brief Save a pattern of cells referenced in save_cells array to a file with
    274  * name and extension .part
    275  */
    276 void file_save_pattern(char *name, int index) {
    277   FILE *f;
    278   char *fname;
    279 
    280   MEM_CHECK(fname = malloc((strlen(name) + 6) * sizeof(char)));
    281   sprintf(fname, "%s.part", name);
    282 
    283   FILE_CHECK(f = fopen(fname, "w"));
    284 
    285   int min_y = save_cells[0]->cord.row;
    286   int min_x = save_cells[0]->cord.col;
    287   for (int i = 0; i < save_cells_s; i++) {
    288     Cell *c = save_cells[i];
    289     min_y = MIN(min_y, c->cord.row);
    290     min_x = MIN(min_x, c->cord.col);
    291   }
    292 
    293   for (int i = 0; i < save_cells_s; i++) {
    294     Cell *c = save_cells[i];
    295     fprintf(f, "%d %d %d\n", c->cord.row - min_y, c->cord.col - min_x, c->val);
    296   }
    297 
    298   fclose(f);
    299 }
    300 
    301 /**
    302  * @brief Load the game from the file with name and extension .all
    303  */
    304 void file_load(char *name, int index) {
    305   FILE *f;
    306   char *fname;
    307   int   w, h;
    308 
    309   MEM_CHECK(fname = malloc((strlen(name) + 5) * sizeof(char)));
    310   sprintf(fname, "%s.all", name);
    311 
    312   FILE_CHECK(f = fopen(fname, "r"));
    313 
    314   fscanf(f, "%d %d %d", &h, &w, &evolve_index);
    315   int row, col, val;
    316   while (fscanf(f, "%d %d %d", &row, &col, &val) != EOF) {
    317     setAt(row, col, val);
    318   }
    319 
    320   game(h, w, evolve_index);
    321 }
    322 
    323 /**
    324  * @brief Save the current game to the file with name and extension .all
    325  */
    326 void file_save(char *name, int index) {
    327   FILE *f;
    328   char *fname;
    329 
    330   MEM_CHECK(fname = malloc((strlen(name) + 5) * sizeof(char)));
    331   sprintf(fname, "%s.all", name);
    332 
    333   FILE_CHECK(f = fopen(fname, "w"));
    334 
    335   fprintf(f, "%d %d %d\n", height, width, evolve_index);
    336   for (Cell *c = hash; c != NULL; c = c->hh.next) {
    337     fprintf(f, "%d %d %d\n", c->cord.row, c->cord.col, c->val);
    338   }
    339 
    340   fclose(f);
    341 }