/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright (C) 2021 KikooDX */ #include "level.h" #include "tiles.h" #include #include #include #define assert(condition, error_msg) \ if (!(condition)) { \ fatal_error = -1; \ fatal_error_msg = error_msg; \ return; \ } #define PATH_PREFIX u"\\\\fls0\\mtem\\" static const uint16_t filepaths[][32] = { PATH_PREFIX "0000.kble", PATH_PREFIX "0001.kble", {0}}; /* globals are needed when using gint_switch() */ struct Level level; int level_id = 0; int fatal_error = 0; char *fatal_error_msg = "no error, happy kikoo :D"; int debug_value = 0; static int read_byte(int file); static Tile read_merge_bytes(int file, int size); /* Load level from storage memory. */ void level_load(void) { int file; int tile_size; int level_size; int file_size; int i; char byte; Tile tile; /* open file read only */ file = BFile_Open(filepaths[level_id], BFile_ReadOnly); assert(file >= 0, "can't open file"); /* assert KBLE format version */ byte = read_byte(file); assert(byte == KBLE_FORMAT_VERSION, "kble format version doesn't match"); /* get tile size (in bytes) */ tile_size = read_byte(file); assert(tile_size >= 0, "can't read tile size"); assert((unsigned int)tile_size <= sizeof(Tile), "tiles are too heavy"); /* assert than width and height are correct */ level.width = read_merge_bytes(file, 2); level.height = read_merge_bytes(file, 2); assert(level.width >= 0, "can't read level width"); assert(level.width == LEVEL_WIDTH, "invalid level width"); assert(level.height >= 0, "can't read level height"); assert(level.height == LEVEL_HEIGHT, "invalid level height"); level_size = level.width * level.height; /* using those informations, see if the file size make sense */ file_size = BFile_Size(file); assert(file_size >= 0, "can't read file size"); assert(file_size == KBLE_HEADER_LEN + level_size * tile_size, "file size doesn't make sense"); /* read file content */ level.gold = 0; level.exit_locked = 0; for (i = 0; i < level_size; i += 1) { tile = read_merge_bytes(file, tile_size); assert(tile != (Tile)-1, "can't read tile"); level.data[i] = tile; /* special tiles */ switch (tile) { case TILE_GOLD: level.gold += 1; break; case TILE_SWITCH: level.exit_locked = 1; break; default: break; } } /* close file */ file = BFile_Close(file); assert(file >= 0, "file closure failed miserably"); } /* Read a single byte. Negative value is BFile error code. */ static int read_byte(int file) { char byte; const int error = BFile_Read(file, &byte, sizeof(char), -1); if (error < 0) return error; else return byte; } /* Read multiple bytes and merge them as one integer. * Return -1 on failure. */ static Tile read_merge_bytes(int file, int size) { Tile merged = 0; int byte = 0; char *byte_ptr = (char *)&merged; int i = 0; byte_ptr += sizeof(Tile) - size; for (i = 0; i < size; i += 1) { byte = read_byte(file); if (byte < 0) return -1; *byte_ptr = byte; byte_ptr += 1; } return merged; }