From ae16f05272b88cbf0ec7aea3d6ef8562eb6d010d Mon Sep 17 00:00:00 2001 From: cancel Date: Sun, 25 Nov 2018 10:07:18 +0900 Subject: [PATCH] Add basic loading of grid fields from file --- base.h | 1 + cli_main.c | 62 +++++++++++++++++++++++++++++++++++++----- field.c | 80 +++++++++++++++++++++++++++++++++++++++++------------- field.h | 12 +++++++- 4 files changed, 128 insertions(+), 27 deletions(-) diff --git a/base.h b/base.h index c899cd9..c8d1a82 100644 --- a/base.h +++ b/base.h @@ -6,6 +6,7 @@ #include #include #include +#include typedef char Term; typedef uint32_t U32; diff --git a/cli_main.c b/cli_main.c index 9fc95ec..0b98c31 100644 --- a/cli_main.c +++ b/cli_main.c @@ -1,15 +1,63 @@ #include "base.h" #include "field.h" -#include +#include int main(int argc, char** argv) { - (void)argc; - (void)argv; + static struct option cli_options[] = {{"time", required_argument, 0, 't'}, + {NULL, 0, NULL, 0}}; + + char* input_file = NULL; + int ticks = 0; + + for (;;) { + int c = getopt_long(argc, argv, "t:", cli_options, NULL); + if (c == -1) + break; + switch (c) { + case 't': + ticks = atoi(optarg); + break; + } + } + + if (optind == argc - 1) { + input_file = argv[optind]; + } + + if (input_file == NULL) { + fprintf(stderr, "No input file\n"); + return 1; + } + if (ticks < 0) { + fprintf(stderr, "Time must be >= 0\n"); + return 1; + } + Field field; - field_init_fill(&field, 32, 32, '.'); - field_fill_subrect(&field, 1, 1, field.height - 2, field.width - 2, 'a'); - field_fill_subrect(&field, 2, 2, field.height - 4, field.width - 4, 'b'); - field_fill_subrect(&field, 3, 3, field.height - 6, field.width - 6, '.'); + field_init(&field); + Field_load_error fle = field_load_file(input_file, &field); + if (fle != Field_load_error_ok) { + field_deinit(&field); + char const* errstr = "Unknown"; + switch (fle) { + case Field_load_error_ok: + break; + case Field_load_error_cant_open_file: + errstr = "Unable to open file"; + break; + case Field_load_error_too_many_columns: + errstr = "Grid file has too many columns"; + break; + case Field_load_error_no_rows_read: + errstr = "Grid file has no rows"; + break; + case Field_load_error_not_a_rectangle: + errstr = "Grid file is not a rectangle"; + break; + } + fprintf(stderr, "File load error: %s\n", errstr); + return 1; + } field_fput(&field, stdout); field_deinit(&field); return 0; diff --git a/field.c b/field.c index f01c6f5..ea835d4 100644 --- a/field.c +++ b/field.c @@ -1,10 +1,10 @@ #include "field.h" +#include -void field_init_zeros(Field* f, U32 height, U32 width) { - size_t num_cells = height * width; - f->buffer = calloc(num_cells, sizeof(Term)); - f->height = height; - f->width = width; +void field_init(Field* f) { + f->buffer = NULL; + f->height = 0; + f->width = 0; } void field_init_fill(Field* f, U32 height, U32 width, Term fill_char) { @@ -22,13 +22,7 @@ void field_resize_raw(Field* f, U32 height, U32 width) { f->width = width; } -void field_deinit(Field* f) { - assert(f->buffer != NULL); - free(f->buffer); -#ifndef NDEBUG - f->buffer = NULL; -#endif -} +void field_deinit(Field* f) { free(f->buffer); } void field_copy_subrect(Field* src, Field* dest, U32 src_y, U32 src_x, U32 dest_y, U32 dest_x, U32 height, U32 width) { @@ -112,7 +106,8 @@ Term field_peek(Field* f, U32 y, U32 x) { size_t f_height = f->height; size_t f_width = f->width; assert(y < f_height && x < f_width); - if (y >= f_height || x >= f_width) return '\0'; + if (y >= f_height || x >= f_width) + return '\0'; return f->buffer[y * f_width + x]; } @@ -120,10 +115,15 @@ void field_poke(Field* f, U32 y, U32 x, Term term) { size_t f_height = f->height; size_t f_width = f->width; assert(y < f_height && x < f_width); - if (y >= f_height || x >= f_width) return; + if (y >= f_height || x >= f_width) + return; f->buffer[y * f_width + x] = term; } +inline bool term_char_is_valid(char c) { + return c >= '#' && c <= '~'; +} + void field_fput(Field* f, FILE* stream) { enum { Column_buffer_count = 4096 }; char out_buffer[Column_buffer_count]; @@ -136,14 +136,56 @@ void field_fput(Field* f, FILE* stream) { Term* row_p = f_buffer + f_width * iy; for (size_t ix = 0; ix < f_width; ++ix) { char c = row_p[ix]; - if (c >= '#' && c <= '~') { - out_buffer[ix] = c; - } else { - out_buffer[ix] = '!'; - } + out_buffer[ix] = term_char_is_valid(c) ? c : '!'; } out_buffer[f_width] = '\n'; out_buffer[f_width + 1] = '\0'; fputs(out_buffer, stream); } } + +Field_load_error field_load_file(char const* filepath, Field* field) { + FILE* file = fopen(filepath, "r"); + if (file == NULL) { + return Field_load_error_cant_open_file; + } + enum { Bufsize = 4096 }; + char buf[Bufsize]; + U32 first_row_columns = 0; + U32 rows = 0; + for (;;) { + char* s = fgets(buf, Bufsize, file); + if (s == NULL) + break; + size_t len = strlen(buf); + if (len == Bufsize - 1 && buf[len - 1] != '\n' && !feof(file)) { + fclose(file); + return Field_load_error_too_many_columns; + } + for (;;) { + if (len == 0) + break; + if (!isspace(buf[len - 1])) + break; + --len; + } + if (len == 0) + continue; + // quick hack until we use a proper scanner + if (rows == 0) { + first_row_columns = len; + } else if (len != first_row_columns) { + fclose(file); + return Field_load_error_not_a_rectangle; + } + field_resize_raw(field, rows + 1, first_row_columns); + Term* rowbuff = field->buffer + first_row_columns * rows; + for (size_t i = 0; i < len; ++i) { + char c = buf[i]; + rowbuff[i] = term_char_is_valid(c) ? c : '!'; + } + ++rows; + } + fclose(file); + return Field_load_error_ok; +} diff --git a/field.h b/field.h index 239d84c..7648816 100644 --- a/field.h +++ b/field.h @@ -1,7 +1,7 @@ #pragma once #include "base.h" -void field_init_zeros(Field* f, U32 height, U32 width); +void field_init(Field* f); void field_init_fill(Field* f, U32 height, U32 width, Term fill_char); void field_resize_raw(Field* f, U32 height, U32 width); void field_deinit(Field* f); @@ -13,3 +13,13 @@ Term field_peek(Field* f, U32 y, U32 x); void field_poke(Field* f, U32 y, U32 x, Term term); void field_fput(Field* f, FILE* stream); + +typedef enum { + Field_load_error_ok = 0, + Field_load_error_cant_open_file = 1, + Field_load_error_too_many_columns = 2, + Field_load_error_no_rows_read = 3, + Field_load_error_not_a_rectangle = 4, +} Field_load_error; + +Field_load_error field_load_file(char const* filepath, Field* field);