2021-03-28 19:35:07 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
|
|
/* Copyright (C) 2021 KikooDX */
|
|
|
|
|
2021-04-09 15:35:31 +02:00
|
|
|
#include "conf.h"
|
2021-04-08 00:38:40 +02:00
|
|
|
#include "filepaths.h"
|
2021-03-28 19:35:07 +02:00
|
|
|
#include "level.h"
|
2021-04-04 17:33:17 +02:00
|
|
|
#include "tiles.h"
|
2021-03-28 19:35:07 +02:00
|
|
|
#include <gint/bfile.h>
|
|
|
|
#include <gint/std/stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#define assert(condition, error_msg) \
|
|
|
|
if (!(condition)) { \
|
|
|
|
fatal_error = -1; \
|
|
|
|
fatal_error_msg = error_msg; \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2021-04-09 15:35:31 +02:00
|
|
|
static int autotile_value(int x, int y);
|
2021-03-28 19:35:07 +02:00
|
|
|
|
|
|
|
/* Load level from storage memory. */
|
|
|
|
void level_load(void)
|
|
|
|
{
|
|
|
|
int file;
|
|
|
|
int tile_size;
|
|
|
|
int level_size;
|
|
|
|
int file_size;
|
|
|
|
int i;
|
2021-04-09 15:35:31 +02:00
|
|
|
int x;
|
|
|
|
int y;
|
2021-03-28 19:35:07 +02:00
|
|
|
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 */
|
2021-04-04 17:33:17 +02:00
|
|
|
level.gold = 0;
|
|
|
|
level.exit_locked = 0;
|
2021-03-28 19:35:07 +02:00
|
|
|
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;
|
2021-04-04 17:33:17 +02:00
|
|
|
/* special tiles */
|
|
|
|
switch (tile) {
|
|
|
|
case TILE_GOLD:
|
|
|
|
level.gold += 1;
|
|
|
|
break;
|
|
|
|
case TILE_SWITCH:
|
|
|
|
level.exit_locked = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-03-28 19:35:07 +02:00
|
|
|
}
|
|
|
|
|
2021-04-09 15:11:00 +02:00
|
|
|
/* set level id for display */
|
|
|
|
level.id = level_id;
|
|
|
|
|
2021-04-09 15:35:31 +02:00
|
|
|
/* compute autotiling */
|
|
|
|
y = level.height;
|
|
|
|
while (y-- > 0) {
|
|
|
|
x = level.width;
|
|
|
|
while (x-- > 0) {
|
|
|
|
level.autotiling[x + y * level.width] =
|
|
|
|
autotile_value(x, y) * TILE_WIDTH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-28 19:35:07 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2021-04-09 15:35:31 +02:00
|
|
|
|
|
|
|
static int autotile_value(int x, int y)
|
|
|
|
{
|
|
|
|
return ((level_get_tile(x - 1, y) == TILE_SOLID) |
|
|
|
|
((level_get_tile(x + 1, y) == TILE_SOLID) << 1) |
|
|
|
|
((level_get_tile(x, y - 1) == TILE_SOLID) << 2) |
|
|
|
|
((level_get_tile(x, y + 1) == TILE_SOLID) << 3));
|
|
|
|
}
|