level loading: bundle levels in binary

This doesn't replace the BFile loading system. This method is safer,
faster and doesn't use gint_switch. The BFile system can be reused for
additionnal features, such as custom user levels (definitly NOT coming
in the first public release).
This commit is contained in:
KikooDX 2021-05-08 00:33:55 +02:00
parent d0adef01e4
commit 25d928cef1
13 changed files with 143 additions and 64 deletions

View File

@ -10,19 +10,7 @@ include(Fxconv)
find_package(Gint 2.4.0 REQUIRED)
find_package(LibImg 2.4.0 REQUIRED)
add_custom_target(filepaths
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS generated/filepaths.h
COMMENT "generated/filepaths.h target")
add_custom_command(OUTPUT "${CMAKE_CURRENT_LIST_DIR}/generated/filepaths.h"
COMMENT "Generate genereated/filepaths.h."
COMMAND mkdir -p generated &&
./make_filepaths.sh > generated/filepaths.h
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
DEPENDS make_filepaths.sh)
include_directories(include generated)
include_directories(include generated/include)
set(SOURCES
src/main.c
@ -59,8 +47,37 @@ set(SOURCES
src/pause/init.c
src/pause/update.c
src/pause/draw.c
generated/filepaths.c
)
set(LEVELS
assets/levels/00/0pillars.kble
assets/levels/00/1breach.kble
assets/levels/00/2human.kble
assets/levels/00/3amas.kble
assets/levels/01/0qujna.kble
assets/levels/01/1greed.kble
assets/levels/01/2dos.kble
assets/levels/01/3secrets.kble
assets/levels/02/0martha.kble
)
add_custom_target(filepaths
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${LEVELS} make_filepaths.sh
COMMENT "filepaths.c & pack_count.h target")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_LIST_DIR}/generated/filepaths.c"
"${CMAKE_CURRENT_LIST_DIR}/generated/include/pack_count.h"
COMMENT "Generate genereated/filepaths.c & "
"generated/include/pack_count.h"
COMMAND mkdir -p generated &&
./make_filepaths.sh > generated/filepaths.c
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
DEPENDS ${LEVELS} make_filepaths.sh)
set(ASSETS
assets/graphics/tileset.png
assets/graphics/trail.png
@ -76,6 +93,7 @@ set(ASSETS
assets/graphics/player/idle.png
assets/graphics/player/jump.png
assets/graphics/player/walk.png
${LEVELS}
)
set(FLAGS
@ -91,6 +109,7 @@ add_dependencies(${PROJECT_NAME} filepaths)
target_compile_options(${PROJECT_NAME} PRIVATE ${FLAGS})
target_link_libraries(${PROJECT_NAME} Gint::Gint)
target_link_libraries(${PROJECT_NAME} LibImg::LibImg)
target_link_options(${PROJECT_NAME} PRIVATE -Wl,-Map=map)
generate_g3a(TARGET ${PROJECT_NAME}
OUTPUT "${PROJECT_NAME}.g3a"

View File

@ -0,0 +1,3 @@
*.kble:
type: binary
name_regex: (.*)\.kble kble_\1

View File

@ -0,0 +1,3 @@
*.kble:
type: binary
name_regex: (.*)\.kble kble_\1

View File

@ -0,0 +1,3 @@
*.kble:
type: binary
name_regex: (.*)\.kble kble_\1

5
include/filepaths.h Normal file
View File

@ -0,0 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2021 KikooDX */
#pragma once
void init_level_binaries(void);

View File

@ -4,6 +4,7 @@
#define LEVEL_WIDTH 25
#define LEVEL_HEIGHT 14
#define LEVEL_SIZE (LEVEL_WIDTH * LEVEL_HEIGHT)
#define KBLE_FORMAT_VERSION 0
#define KBLE_HEADER_LEN 6
@ -28,6 +29,7 @@ struct Level {
};
/* need to set global before call: level_id */
void level_load(void);
void level_load_bfile(void);
void level_load_binary(void);
void level_draw(void);
Tile level_get_tile(int x, int y);

View File

@ -2,20 +2,22 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2021 KikooDX
printf '/* SPDX-License-Identifier: GPL-3.0-or-later */\n'
printf '#pragma once\n'
printf '#include <stdint.h>\n\n'
printf 'static const uint16_t filepaths[][64] = {\n'
START_POS="$PWD"
LEVEL_COUNT=0
cd assets/levels
cd assets/levels || exit
for PACK in *; do
PACK_NAME="$(basename $PACK)"
cd "$PACK"
PACK_NAME="$(basename "$PACK")"
cd "$PACK" || exit
for LEVEL in *; do
LEVEL_COUNT="$(($LEVEL_COUNT + 1))"
LEVEL_NAME="$(basename $LEVEL | cut -c2- |
[ "$(basename $LEVEL)" == fxconv-metadata.txt ] && continue
LEVEL_ARRAY="$(printf '%s\n\tlevel_binaries[%s] = %s;' \
"$LEVEL_ARRAY" "$LEVEL_COUNT" \
"kble_$(basename "$LEVEL" .kble)")"
EXTERN_LEVEL="$(printf '%s\nextern uint8_t %s[];' "$EXTERN_LEVEL" \
"kble_$(basename "$LEVEL" .kble)")"
LEVEL_COUNT="$((LEVEL_COUNT + 1))"
LEVEL_NAME="$(basename "$LEVEL" | cut -c2- |
awk -F '.' '{print $1}' |
tr '[:lower:]' '[:upper:]')"
[ -z "$LEVEL_NAMES" ] &&
@ -23,15 +25,26 @@ for PACK in *; do
\"$LEVEL_NAME\"" ||
LEVEL_NAMES="$LEVEL_NAMES,
\"$LEVEL_NAME\""
printf '\tu"\\\\\\\\fls0\\\\mtem\\\\%s\\\\' "$PACK_NAME"
printf '%s",\n' "$LEVEL"
done
cd ..
done
printf '};\n\n'
printf 'static const char level_names[][32] = {%s\n};\n\n' "$LEVEL_NAMES"
printf '/* SPDX-License-Identifier: GPL-3.0-or-later */\n'
printf '/* This code was generated with `make_filepaths.sh` */\n'
printf '#include "filepaths.h"\n'
printf '#include <stdint.h>\n'
[ "$(($LEVEL_COUNT % 4))" != 0 ] && LEVEL_COUNT="$(($LEVEL_COUNT + 4 - $LEVEL_COUNT % 4))"
printf '%s\n\n' "$EXTERN_LEVEL"
printf 'uint8_t *level_binaries[%s];\n\n' "$LEVEL_COUNT"
printf '#define PACK_COUNT %s\n' "$(($LEVEL_COUNT / 4))"
printf 'char *const level_names[%s] = {%s\n};\n\n' "$LEVEL_COUNT" "$LEVEL_NAMES"
# init_level_binaries
printf 'void\ninit_level_binaries(void)\n{'
printf '%s\n}\n' "$LEVEL_ARRAY"
# Create headers
cd "$START_POS"
mkdir -p generated/include
[ "$((LEVEL_COUNT % 4))" != 0 ] && LEVEL_COUNT="$((LEVEL_COUNT + 4 - LEVEL_COUNT % 4))"
printf '#define PACK_COUNT %s\n' "$((LEVEL_COUNT / 4))" >> generated/include/pack_count.h

View File

@ -2,7 +2,6 @@
/* Copyright (C) 2021 KikooDX */
#include "conf.h"
#include "filepaths.h"
#include "level.h"
#include "tiles.h"
#include <gint/bfile.h>
@ -23,26 +22,27 @@ int fatal_error = 0;
char *fatal_error_msg = "no error, happy kikoo :D";
int debug_value = 0;
static uint16_t *filepath = u"none";
static void level_load_post(void);
static int read_byte(int file);
static Tile read_merge_bytes(int file, int size);
static int autotile_value(int x, int y);
/* Load level from storage memory. */
void
level_load(void)
level_load_bfile(void)
{
int file;
int tile_size;
int level_size;
int file_size;
int i;
int x;
int y;
char byte;
uint8_t byte;
Tile tile;
/* open file read only */
file = BFile_Open(filepaths[level_id], BFile_ReadOnly);
file = BFile_Open(filepath, BFile_ReadOnly);
assert(file >= 0, "can't open file");
/* assert KBLE format version */
@ -71,35 +71,68 @@ level_load(void)
"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;
}
}
level_load_post();
/* close file */
file = BFile_Close(file);
assert(file >= 0, "file closure failed miserably");
}
/* Load level bundled in game binary. */
void
level_load_binary(void)
{
extern uint8_t *level_binaries[];
uint8_t *level_binary = level_binaries[level_id];
int i;
i = LEVEL_SIZE;
while (i-- > 0)
level.data[i] = level_binary[KBLE_HEADER_LEN + i];
level.width = LEVEL_WIDTH;
level.height = LEVEL_HEIGHT;
level_load_post();
}
/* Shared post processing between BFile and binary level load. */
static void
level_load_post(void)
{
Tile tile;
int x;
int y;
level.gold = 0;
level.exit_locked = 0;
/* set level id for display */
level.id = level_id;
/* compute visuals */
y = level.height;
i = 0;
while (y-- > 0) {
x = level.width;
while (x-- > 0) {
tile = level.data[x + y * level.width];
/* special tiles */
switch (tile) {
case TILE_GOLD:
level.gold += 1;
break;
case TILE_SWITCH:
level.exit_locked = 1;
break;
default:
break;
}
/* compute visuals */
struct VisualTile visual_data;
visual_data.x = x * TILE_WIDTH;
visual_data.y = y * TILE_HEIGHT;
@ -120,18 +153,14 @@ level_load(void)
level.visual_data[x + y * level.width] = visual_data;
}
}
/* 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);
uint8_t byte;
const int error = BFile_Read(file, &byte, sizeof(uint8_t), -1);
if (error < 0)
return error;
else
@ -145,7 +174,7 @@ read_merge_bytes(int file, int size)
{
Tile merged = 0;
int byte = 0;
char *byte_ptr = (char *)&merged;
uint8_t *byte_ptr = (uint8_t *)&merged;
int i = 0;
byte_ptr += sizeof(Tile) - size;
for (i = 0; i < size; i += 1) {

View File

@ -2,8 +2,8 @@
/* Copyright (C) 2021 KikooDX */
#include "conf.h"
#include "filepaths.h"
#include "levelselection.h"
#include "pack_count.h"
#include "util.h"
#include "zxcolors.h"
#include <gint/display.h>

View File

@ -1,9 +1,9 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2021 KikooDX */
#include "filepaths.h"
#include "input.h"
#include "levelselection.h"
#include "pack_count.h"
#include "util.h"
/* Return 1 if game state should change. */

View File

@ -2,6 +2,7 @@
/* Copyright (C) 2021 KikooDX */
#include "conf.h"
#include "filepaths.h"
#include "game_state.h"
#include "input.h"
#include "level.h"
@ -31,7 +32,7 @@
} while (0);
#define LOAD_LEVEL() \
do { \
gint_world_switch(GINT_CALL(level_load)); \
gint_world_switch(GINT_CALL(level_load_binary)); \
if (fatal_error == -1) \
PANIC(fatal_error_msg); \
particles_init(); \
@ -73,9 +74,11 @@ main(void)
/* set font */
dfont(&font_main);
init_level_binaries();
/* load level */
level_id = 0;
gint_world_switch(GINT_CALL(level_load));
gint_world_switch(GINT_CALL(level_load_binary));
if (fatal_error == -1)
PANIC(fatal_error_msg);
/* timer setup */

View File

@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2021 KikooDX */
#include "filepaths.h"
#include "input.h"
#include "mainmenu.h"

View File

@ -2,7 +2,6 @@
/* Copyright (C) 2021 KikooDX */
#include "conf.h"
#include "filepaths.h"
#include "level.h"
#include "particles.h"
#include "player.h"
@ -10,6 +9,7 @@
#include <gint/display.h>
extern struct Level level;
extern char *const level_names[];
void
player_draw(struct Player player)