diff --git a/example-bundle.txt b/example-bundle.txt new file mode 100644 index 0000000..57fe848 --- /dev/null +++ b/example-bundle.txt @@ -0,0 +1,26 @@ +Example bundle + +Stages are separated by ten dashes, and each stage must be 10 lines of 10 +characters each. At the top of the file is a free-form comment field. +---------- + + + + + + + + + ! +gggggggggg +---------- + ! + D 4 +C 3 44 +3 3 4 + B + 222 2 2 + + A 1 111 +@g +gd diff --git a/switcher.c b/switcher.c index 6b1e603..210902c 100644 --- a/switcher.c +++ b/switcher.c @@ -17,6 +17,7 @@ enum game_mode { TITLESCREEN, STAGE }; enum game_mode game_mode = TITLESCREEN; bool stages_beat[STAGES]; +size_t stages_num; #define PLAYFIELD_SIDE 100 #define TILE_SIDE 10 @@ -93,7 +94,8 @@ static enum palette playfield[PLAYFIELD_SIDE * PLAYFIELD_SIDE]; #include "rebind_rebind1_alt_tile.inc" static char tilemap[TILES * TILES + 1]; -static char *stages[] = { +static char *stages[STAGES]; +static char *builtin_stages[] = { " " " " " " @@ -446,7 +448,7 @@ static void update_stage(struct timespec now, struct timespec dt_timespec) { switch(sample_tilemap(midpoint_x, midpoint_y)) { case '!': stages_beat[selection_index] = true; - if (selection_index < STAGES - 1) + if (selection_index + 1 < stages_num) selection_index++; game_mode = TITLESCREEN; initialize(); @@ -473,7 +475,7 @@ static void update_titlescreen(struct timespec now, struct timespec dt_timespec) (void) dt_timespec; if (keys_released && left_held && selection_index > 0) selection_index--; - if (keys_released && right_held && selection_index < STAGES - 1) + if (keys_released && right_held && selection_index + 1 < stages_num) selection_index++; if (keys_released && jump_held) stage_selected = true; @@ -549,20 +551,20 @@ static void draw_stage(struct timespec now) { static void draw_titlescreen(struct timespec now) { (void) now; - for (size_t i = 0; i < STAGES; i++) { + for (size_t i = 0; i < stages_num; i++) { size_t x = i * TILE_SIDE + TILE_SIDE; size_t y = 0; if (stages_beat[i]) draw_tile(star_tile, x, y); } - draw_tile(stage1_tile, 1 * TILE_SIDE, TILE_SIDE); - draw_tile(stage2_tile, 2 * TILE_SIDE, TILE_SIDE); - draw_tile(stage3_tile, 3 * TILE_SIDE, TILE_SIDE); - draw_tile(stage4_tile, 4 * TILE_SIDE, TILE_SIDE); - draw_tile(stage5_tile, 5 * TILE_SIDE, TILE_SIDE); - draw_tile(stage6_tile, 6 * TILE_SIDE, TILE_SIDE); - draw_tile(stage7_tile, 7 * TILE_SIDE, TILE_SIDE); - draw_tile(stage8_tile, 8 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 1) draw_tile(stage1_tile, 1 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 2) draw_tile(stage2_tile, 2 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 3) draw_tile(stage3_tile, 3 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 4) draw_tile(stage4_tile, 4 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 5) draw_tile(stage5_tile, 5 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 6) draw_tile(stage6_tile, 6 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 7) draw_tile(stage7_tile, 7 * TILE_SIDE, TILE_SIDE); + if (stages_num >= 8) draw_tile(stage8_tile, 8 * TILE_SIDE, TILE_SIDE); size_t underline_y = 2 * TILE_SIDE + 1; size_t underline_x = selection_index * TILE_SIDE + TILE_SIDE + 2; for (size_t x = 0; x < TILE_SIDE - 4; x++) @@ -605,7 +607,91 @@ static void draw(struct timespec now) { } } +static void load_stages(char *path) { + FILE *f = fopen(path, "r"); + if (!f) + err(1, "%s", path); + + stages_num = 0; + + bool in_comment = true; + size_t linenum = 0; + size_t stage_fill = 0; + char *stage[10]; + while (true) { + linenum++; + char *line = NULL; + size_t size = 0; + ssize_t length = getline(&line, &size, f); + if (length < 0) { + break; + free(line); + } + if (line[length - 1] == '\n') { + line[length - 1] = 0; + length--; + } + if (!strcmp(line, "----------")) { + if (in_comment) { + in_comment = false; + continue; + } else { + if (stages_num == STAGES) + errx(1, "%s: %zu: Too many stages (maximum %zu)", path, linenum, (size_t)STAGES); + free(line); + if (stage_fill < 10) + errx(1, "%s: %zu: Not enough lines for a stage (expected 10, got %zu)", path, linenum, stage_fill); + char *stage_str; + asprintf(&stage_str, "%s%s%s%s%s%s%s%s%s%s", stage[0], stage[1], stage[2], stage[3], stage[4], stage[5], stage[6], stage[7], stage[8], stage[9]); + stages[stages_num++] = stage_str; + for (size_t i = 0; i < stage_fill; i++) + free(stage[i]); + stage_fill = 0; + continue; + } + } else if (stage_fill == 10) + errx(1, "%s: %zu: Too many lines for a stage (expected 10)", path, linenum); + if (in_comment) + free(line); + else if (length < 10) + errx(1, "%s: %zu: Line too short (expected 10 character, got %zd)", path, linenum, length); + else if (length > 10) + errx(1, "%s: %zu: Line too long (expected 10 character, got %zd)", path, linenum, length); + else + stage[stage_fill++] = line; + } + + if (stage_fill) { + if (stages_num == STAGES) + errx(1, "%s: %zu: Too many stages (maximum %zu)", path, linenum, (size_t)STAGES); + if (stage_fill < 10) + errx(1, "%s: %zu: Not enough lines for a stage (expected 10, got %zu)", path, linenum, stage_fill); + char *stage_str; + asprintf(&stage_str, "%s%s%s%s%s%s%s%s%s%s", stage[0], stage[1], stage[2], stage[3], stage[4], stage[5], stage[6], stage[7], stage[8], stage[9]); + stages[stages_num++] = stage_str; + for (size_t i = 0; i < stage_fill; i++) + free(stage[i]); + } + + if (!stages_num) + errx(1, "%s: Bundle did not contain any stages", path); + + fclose(f); +} + int main(int argc, char *argv[]) { + if (argc == 1) { + stages_num = sizeof(builtin_stages) / sizeof(*builtin_stages); + for (size_t i = 0; i < stages_num; i++) { + stages[i] = builtin_stages[i]; + } + } else if (argc == 2) + load_stages(argv[1]); + else { + printf("Usage: %s [/path/to/stage-bundle]\n", argv[0]); + return 1; + } + struct display_connection *connection = display_connect_default(); if (!connection && errno == ECONNREFUSED) display_spawn(argc, argv);