/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright (C) 2021 KikooDX */ #include "editing_area/level.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, char byte); static int write_byte_group(FILE *file, Tile value, int size); int level_read(struct Level *level, char *path) { FILE *file = NULL; 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) */ tile_size = read_byte(file); if (tile_size < 0) return -1; /* 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 (level->width == (Tile)-1) return -1; /* get height */ level->height = read_byte_group(file, 2); if (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; } void level_free(struct Level *level) { free(level->data); } int level_write(struct Level level, char *path) { FILE *file = NULL; int tile = 0; int tile_size = 0; 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; } /* 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; } /* 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); 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 (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; char *byte_ptr = (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, 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) { char *value_ptr = (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; }