From 4c771227f5fe93cac7460af627e4c0d0ad5631d9 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Fri, 8 May 2026 23:48:35 -0400 Subject: [PATCH] Encode library version and game version into savegame files and check them both --- include/akgl/SDL_GameControllerDB.h | 2 +- include/akgl/game.h | 7 +++-- src/game.c | 48 +++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/include/akgl/SDL_GameControllerDB.h b/include/akgl/SDL_GameControllerDB.h index 87fc184..57c03d5 100644 --- a/include/akgl/SDL_GameControllerDB.h +++ b/include/akgl/SDL_GameControllerDB.h @@ -1,7 +1,7 @@ #ifndef _SDL_GAMECONTROLLERDB_H_ #define _SDL_GAMECONTROLLERDB_H_ -// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Fri May 8 11:02:47 PM EDT 2026 +// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Fri May 8 11:47:47 PM EDT 2026 #define AKGL_SDL_GAMECONTROLLER_DB_LEN 2227 diff --git a/include/akgl/game.h b/include/akgl/game.h index 06a320e..0200adc 100644 --- a/include/akgl/game.h +++ b/include/akgl/game.h @@ -6,8 +6,10 @@ #include #include "tilemap.h" -#define AKGL_GAME_AUDIO_TRACK_BGM 1 -#define AKGL_GAME_AUDIO_MAX_TRACKS 64 +#define AKGL_VERSION "0.1.0" + +#define AKGL_GAME_AUDIO_TRACK_BGM 1 +#define AKGL_GAME_AUDIO_MAX_TRACKS 64 #define AKGL_TIME_ONESEC_NS 1000000000 #define AKGL_TIME_ONESEC_MS 1000000 @@ -25,6 +27,7 @@ typedef struct { } akgl_GameState; typedef struct { + char libversion[32]; char version[32]; char name[256]; char uri[256]; diff --git a/src/game.c b/src/game.c index f2318bc..e226778 100644 --- a/src/game.c +++ b/src/game.c @@ -34,6 +34,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init() int i = 0; PREPARE_ERROR(errctx); ATTEMPT { + strncpy((char *)&game.libversion, AKGL_VERSION, 32); game.gameStartTime = SDL_GetTicksNS(); game.lastIterTime = game.gameStartTime; game.lastFPSTime = game.gameStartTime; @@ -135,24 +136,23 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath) SUCCEED_RETURN(errctx); // SUCCEED_NORETURN if in main(). } -akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath) +akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(FILE *fp, const char *versiontype, char *curversion) { - FILE *fp = NULL; char versionstr[32]; semver_t current_version = {}; semver_t compare_version = {}; - PREPARE_ERROR(errctx); - FAIL_ZERO_RETURN(errctx, fpath, AKERR_NULLPOINTER, "NULL file path"); + FAIL_ZERO_RETURN(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer"); ATTEMPT { + // Check save game library version FAIL_NONZERO_BREAK( errctx, - semver_parse((const char *)&game.version, ¤t_version), + semver_parse((const char *)curversion, ¤t_version), AKERR_VALUE, - "Invalid semantic version in current game: %s", - (char *)¤t_version); - fp = fopen(fpath, "rb"); + "Invalid semantic %s version in current game: %s", + versiontype, + (char *)curversion); FAIL_NONZERO_BREAK( errctx, (fread((void *)&versionstr, 1, 32, fp) < 32), @@ -162,13 +162,37 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath) errctx, semver_parse((const char *)&versionstr, &compare_version), AKERR_VALUE, - "Invalid semantic version in save game: %s", + "Invalid semantic %s version in save game: %s", + versiontype, (char *)&versionstr); FAIL_ZERO_BREAK( errctx, - semver_satisfies(compare_version, current_version, "^"), + semver_satisfies(compare_version, current_version, "="), AKERR_API, - "Incompatible save game version"); + "Incompatible save game %s version (%s ^ %s)", + versiontype, + curversion, + (char *)&versionstr); + } CLEANUP { + semver_free(¤t_version); + semver_free(&compare_version); + } PROCESS(errctx) { + } FINISH(errctx, true); + SUCCEED_RETURN(errctx); +} + +akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath) +{ + FILE *fp = NULL; + + PREPARE_ERROR(errctx); + FAIL_ZERO_RETURN(errctx, fpath, AKERR_NULLPOINTER, "NULL file path"); + + ATTEMPT { + fp = fopen(fpath, "rb"); + FAIL_ZERO_BREAK(errctx, fp, errno, "%s", fpath); + CATCH(errctx, akgl_game_load_versioncmp(fp, "library", (char *)AKGL_VERSION)); + CATCH(errctx, akgl_game_load_versioncmp(fp, "game", (char *)&game.version)); rewind(fp); FAIL_NONZERO_BREAK( errctx, @@ -179,8 +203,6 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath) if ( fp != NULL ) { fclose(fp); } - semver_free(¤t_version); - semver_free(&compare_version); } PROCESS(errctx) { } FINISH(errctx, true);