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 }