/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright (C) 2021 KikooDX */ #include "editing_area/level.h" #include "info.h" #include "options.h" #include #include #include static const int kble_fmt_version = 0; static int read_byte(FILE *file); static Tile read_byte_group(FILE *file, int size); static int write_byte(FILE *file, unsigned char byte); static int write_byte_group(FILE *file, Tile value, int size); int level_read(struct Level *level, char *path) { FILE *file = NULL; int byte_hold = 0; unsigned char byte = 0; unsigned int tile_size = 0; int level_size = 0; int i = 0; Tile tile = 0; /* open file in read mode */ file = fopen(path, "rb"); if (file == NULL) { fprintf(stderr, "ERROR: cannot open input file %s\n", path); return -1; } /* check KBLE format version */ byte = read_byte(file); if (byte != kble_fmt_version) { fprintf(stderr, "ERROR: KBLE format version doesn't match ; " "expected %d, got %d\n", kble_fmt_version, byte); return -1; } /* get tile size (in bytes) */ byte_hold = read_byte(file); if (byte_hold < 0) return -1; tile_size = (unsigned int)byte_hold; /* check than tile size is in boundaries */ if (tile_size > sizeof(Tile) / sizeof(char)) { fprintf(stderr, "ERROR: tile size is too big ; " "maximum is %ld, found %d", sizeof(Tile), tile_size); return -1; } /* get width */ level->width = read_byte_group(file, 2); if ((Tile)level->width == (Tile)-1) return -1; /* get height */ level->height = read_byte_group(file, 2); if ((Tile)level->height == (Tile)-1) return -1; /* allocate memory for data */ level_size = level->width * level->height; if (level->data == NULL) level->data = malloc(level_size * sizeof(Tile)); else level->data = realloc(level->data, level_size * sizeof(Tile)); /* check for allocation failure */ if (level->data == NULL) { fprintf(stderr, "ERROR: memory allocation failure\n"); return -1; } /* read file content */ for (i = 0; i < level_size; i += 1) { tile = read_byte_group(file, tile_size); level->data[i] = tile; } /* close file */ fclose(file); return 0; } int level_create(struct Level *level, struct Options options) { int level_size; int i; /* set width */ level->width = options.new_level_width; if ((Tile)level->width == (Tile)-1) return -1; /* set height */ level->height = options.new_level_height; if ((Tile)level->height == (Tile)-1) return -1; /* allocate memory for data */ level_size = level->width * level->height; if (level->data == NULL) level->data = malloc(level_size * sizeof(Tile)); else level->data = realloc(level->data, level_size * sizeof(Tile)); /* check for allocation failure */ if (level->data == NULL) { fprintf(stderr, "ERROR: memory allocation failure\n"); return -1; } /* file the level with zeros */ for (i = 0; i < level_size; i += 1) level->data[i] = 0; return 0; } void level_free(struct Level *level) { free(level->data); } int level_write(struct Level level, char *path) { INFO("write begin"); FILE *file = NULL; int tile = 0; unsigned int tile_size = 0; unsigned int max_tile_size = 1; int i = 0; const int level_size = level.width * level.height; /* open file in write mode */ file = fopen(path, "wb"); if (file == NULL) { fprintf(stderr, "ERROR: cannot open output file %s\n", path); return -1; } INFO("file opened"); INFO("look for longest value in data"); /* find longest value in data (in bytes) */ for (i = 0; i < level_size; i += 1) { tile = level.data[i]; tile_size = 1; while (tile >>= 8) tile_size += 1; if (tile_size > max_tile_size) { max_tile_size = tile_size; INFO_VAR("%d", max_tile_size); } } INFO("max tile size is"); INFO_VAR("%d", max_tile_size); /* write KBLE format version */ write_byte(file, kble_fmt_version); /* write tile size (in bytes) */ write_byte(file, max_tile_size); /* write width */ write_byte_group(file, level.width, 2); /* write height */ write_byte_group(file, level.height, 2); /* write level content */ for (i = 0; i < level_size; i += 1) { write_byte_group(file, level.data[i], max_tile_size); } /* close file */ fclose(file); INFO("write end"); return 0; } /* Read a single byte safely (handle EOF). Return -1 if error. */ static int read_byte(FILE *file) { const int byte = fgetc(file); if (byte == EOF) { fprintf(stderr, "ERROR: unexpected EOF\n"); return -1; } return (unsigned char)byte; } /* Read multiple bytes and "merge" them into one integer. Return -1 if * error. */ static Tile read_byte_group(FILE *file, int size) { int group = 0; unsigned char *byte_ptr = (unsigned char *)&group; int value = 0; int i = 0; byte_ptr += size; for (i = 0; i < size; i += 1) { value = read_byte(file); if (value < 0) return -1; byte_ptr -= 1, *byte_ptr = value; } return group; } /* Write a single byte safely (handle EOF). Return -1 if error. */ static int write_byte(FILE *file, unsigned char byte) { const int result = fputc(byte, file); if (result == EOF) { fprintf(stderr, "ERROR: file write error\n"); return -1; } return 0; } /* Write an integer as multiple bytes. Return -1 if error. */ static int write_byte_group(FILE *file, Tile value, int size) { unsigned char *value_ptr = (unsigned char *)&value; value_ptr += size; int error; int i = 0; for (i = 0; i < size; i += 1) { value_ptr -= 1; error = write_byte(file, *value_ptr); if (error < 0) return -1; } return 0; }