From 1dc9f1129ca715e0842eba3f7bae7a0dc0f53d54 Mon Sep 17 00:00:00 2001 From: Spencer Vaughn Date: Sat, 2 Mar 2024 08:26:05 -0600 Subject: [PATCH] Update version number and fix function names --- .github/workflows/build-macos.yml | 10 +++ .gitignore | 6 +- .vscode/c_cpp_properties.json | 3 +- example/include/print.h | 4 +- example/src/main.c | 16 ++--- example/src/print.c | 16 ++--- lib/include/wariosave/common.h | 85 ++++++++++++++---------- lib/include/wariosave/helpers.h | 60 ++++++++--------- lib/makefile | 2 +- lib/src/wariosave.c | 90 ++++++++++++------------- test/makefile | 20 ++++++ test/saves/wario.sav | Bin 0 -> 8192 bytes test/src/ws_helpers.c | 105 ++++++++++++++++++++++++++++++ 13 files changed, 287 insertions(+), 130 deletions(-) create mode 100644 test/makefile create mode 100644 test/saves/wario.sav create mode 100644 test/src/ws_helpers.c diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 9a95669..765ff0b 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -42,12 +42,22 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v3 + + - name: Install Test tools + run: | + brew install cunit cppcheck - name: Build wariosave run: | cd lib mkdir -p build/release make release + + - name: Run Tests + run: | + cppcheck --std=c11 --enable=all --inconclusive --suppress=missingInclude --suppress=missingIncludeSystem --suppress=unusedFunction lib/src/ || true + cd test/ + make test - name: Publish Build Artifacts uses: actions/upload-artifact@v2 diff --git a/.gitignore b/.gitignore index fca3acd..7b00c62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ +# lib and example build folder build/ deps/ -.DS_Store \ No newline at end of file +.DS_Store + +# test executable +wariosave_test \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 6b547d9..8199129 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -5,7 +5,8 @@ "includePath": [ "${workspaceFolder}/**", "${workspaceFolder}/example/include", - "${workspaceFolder}/lib/include" + "${workspaceFolder}/lib/include", + "/opt/homebrew/Cellar/cunit/2.1-3/include" ], "defines": [], "macFrameworkPath": [ diff --git a/example/include/print.h b/example/include/print.h index 849fd62..b33fcab 100644 --- a/example/include/print.h +++ b/example/include/print.h @@ -9,7 +9,7 @@ #include -void print_player_save(PlayerSave *save); -void print_save_data(WarioSave *save); +void print_player_save(WS_PlayerSave *save); +void print_save_data(WS_WarioSave *save); #endif // PRINT_H diff --git a/example/src/main.c b/example/src/main.c index c1acc50..95cbaec 100644 --- a/example/src/main.c +++ b/example/src/main.c @@ -10,11 +10,11 @@ int main(int argc, char *argv[]) { // Complete savefile buffer - WarioGameSave game_save; + WS_WarioGameSave game_save; // Save slot buffer (3 file slots) - WarioSave save_file[FILE_COUNT]; + WS_WarioSave save_file[WS_MAX_FILE_SLOT_COUNT]; // Formatted player save data (3 player saves) - PlayerSave player_save[FILE_COUNT]; + WS_PlayerSave player_save[WS_MAX_FILE_SLOT_COUNT]; if (argc != 2) { @@ -23,17 +23,17 @@ int main(int argc, char *argv[]) } const char *file_path = argv[1]; - int error = load_save_to_buffer(&game_save, file_path); + WS_FileError error = ws_load_save_to_buffer(&game_save, file_path); if (error != NO_ERROR) { return error; } - for (int fileSlot = FILE_A; fileSlot < FILE_COUNT; fileSlot++) + for (uint8_t file_slot = FILE_A; file_slot < FILE_COUNT; file_slot++) { - save_file[fileSlot] = game_save.save[fileSlot]; - initialize_player_save(&save_file[fileSlot], &player_save[fileSlot]); - print_player_save(&player_save[fileSlot]); + save_file[file_slot] = game_save.save[file_slot]; + ws_initialize_player_save(&save_file[file_slot], &player_save[file_slot]); + print_player_save(&player_save[file_slot]); } return 0; diff --git a/example/src/print.c b/example/src/print.c index c89811a..7bf2ffe 100644 --- a/example/src/print.c +++ b/example/src/print.c @@ -6,31 +6,31 @@ #include "print.h" -void print_treasures_obtained(PlayerSave *player_save) +void print_treasures_obtained(WS_PlayerSave *player_save) { - for (int i = 0; i < MAX_TREASURE_COUNT; i++) + for (int i = 0; i < WS_MAX_TREASURE_COUNT; i++) { - printf("%c ", player_save->treasure.obtained[i] ? treasure_names[i] : '-'); - if (i >= MAX_TREASURE_COUNT - 1) + printf("%c ", player_save->treasure.obtained[i] ? WS_treasure_names[i] : '-'); + if (i >= WS_MAX_TREASURE_COUNT - 1) { printf("\n"); } } } -void print_player_save(PlayerSave *save) { +void print_player_save(WS_PlayerSave *save) { printf("Total Coins: %u\n", save->total_coins); printf("Hearts: %d\n", save->hearts); printf("Lives: %d\n", save->lives); printf("Game Completed: %s\n", save->game_completed ? "true" : "false"); - for (int i = 0; i < LEVELS_COUNT; i++) { - printf("%s: %d%%\n", default_level_data[i].name, save->levels[i].completion_rate); + for (int i = 0; i < WS_LEVELS_COUNT; i++) { + printf("%s: %d%%\n", WS_default_level_data[i].name, save->levels[i].completion_rate); } printf("Treasure completion rate: %d%%\n", save->treasure.completion_rate); print_treasures_obtained(save); } -void print_save_data(WarioSave *save) +void print_save_data(WS_WarioSave *save) { printf("sLevelId: %d\n", save->sLevelId); printf("sTotalCoins_High: %02x\n", save->sTotalCoins_High); diff --git a/lib/include/wariosave/common.h b/lib/include/wariosave/common.h index 6e756e0..5e96951 100644 --- a/lib/include/wariosave/common.h +++ b/lib/include/wariosave/common.h @@ -4,8 +4,8 @@ * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ -#ifndef COMMON_H -#define COMMON_H +#ifndef WS_COMMON_H +#define WS_COMMON_H #include #include @@ -23,29 +23,37 @@ ((byte) & 0x02 ? '1' : '0'), \ ((byte) & 0x01 ? '1' : '0') -#define SAVE_FILE_A_OFFSET 0x04 -#define SAVE_FILE_B_OFFSET 0x44 -#define SAVE_FILE_C_OFFSET 0x84 -#define SAVE_FILE_OFFSET 0x40 -#define SAVE_STRUCT_SIZE 0x14 -#define LEVELS_COUNT 7 -#define MAX_COURSE_COUNT 8 -#define MAX_LVL_NAME_CHAR 14 -#define MAX_COURSE_NAME_CHAR 18 -#define MAX_COIN_DIGITS_STR 7 -#define MAX_TREASURE_COUNT 15 +#define WS_SAVE_FILE_A_OFFSET 0x04 +#define WS_SAVE_FILE_B_OFFSET 0x44 +#define WS_SAVE_FILE_C_OFFSET 0x84 +#define WS_SAVE_FILE_OFFSET 0x40 +#define WS_SAVE_STRUCT_SIZE 0x14 +#define WS_LEVELS_COUNT 7 +#define WS_MAX_COURSE_COUNT 8 +#define WS_MAX_LVL_NAME_CHAR 14 +#define WS_MAX_COURSE_NAME_CHAR 18 +#define WS_MAX_COIN_DIGITS_STR 7 +#define WS_MAX_TREASURE_COUNT 15 +#define WS_MAX_FILE_SLOT_COUNT 3 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; +/** + * WS_Level data structure + * name: Name of the level + * course_exit_count: Number of course exits in the level + * completion_bitmask: 8-bit bitmask of course completion + * completion_rate: Completion rate of the level in percentage +*/ typedef struct { /** * Name of the level * Max length of 14 characters */ - char name[MAX_LVL_NAME_CHAR]; + char name[WS_MAX_LVL_NAME_CHAR]; /** * Number of course exits in the level */ @@ -58,14 +66,14 @@ typedef struct * Completion rate of the level in percentage */ uint8_t completion_rate; -} Level; +} WS_Level; /** * Default level data * name: Name of the level * course_exit_count: Number of course exits in the level */ -static const Level default_level_data[LEVELS_COUNT] = { +static const WS_Level WS_default_level_data[WS_LEVELS_COUNT] = { {.name = "Rice Beach", .course_exit_count = 7, .completion_bitmask = 0, @@ -99,15 +107,15 @@ static const Level default_level_data[LEVELS_COUNT] = { typedef struct { uint8_t sLevelId; - // Values 99xxxx stored in dec format. Use get_decimal_bytes to convert to int + // Values 99xxxx stored in dec format. Use ws_get_decimal_bytes to convert to int uint8_t sTotalCoins_High; - // Values xx99xx stored in dec format. Use get_decimal_bytes to convert to int + // Values xx99xx stored in dec format. Use ws_get_decimal_bytes to convert to int uint8_t sTotalCoins_Mid; - // Values xxxx99 stored in dec format. Use get_decimal_bytes to convert to int + // Values xxxx99 stored in dec format. Use ws_get_decimal_bytes to convert to int uint8_t sTotalCoins_Low; - // Value stored in memory in dec format. Use get_decimal_byte to convert to int + // Value stored in memory in dec format. Use ws_get_decimal_byte to convert to int uint8_t sHearts; - // Value stored in memory in dec format. Use get_decimal_byte to convert to int + // Value stored in memory in dec format. Use ws_get_decimal_byte to convert to int uint8_t sLives; // Current powerup value uint8_t sPlPower; @@ -138,11 +146,14 @@ typedef struct * 1-bit flag of game completion */ uint8_t sGameCompleted; -} WarioSave; +} WS_WarioSave; -typedef struct { WarioSave save[3]; } WarioGameSave; +/** + * All three save slots copied into one buffer +*/ +typedef struct { WS_WarioSave save[WS_MAX_FILE_SLOT_COUNT]; } WS_WarioGameSave; -static const char treasure_names[MAX_TREASURE_COUNT] = { +static const char WS_treasure_names[WS_MAX_TREASURE_COUNT] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' }; typedef enum { @@ -161,8 +172,14 @@ typedef enum { TREASURE_M, TREASURE_N, TREASURE_O -} TreasureNames; +} WS_TreasureNames; +/** + * WS_Treasure data structure + * count: count of treasures obtained + * completion_rate: completion rate of the treasures in percentage + * obtained: array of bools representing if the treasure was obtained +*/ typedef struct { /** * count of treasures obtained @@ -174,11 +191,11 @@ typedef struct { uint8_t completion_rate; /** * array of bools representing if the treasure was obtained - * accessed with TreasureNames enum + * accessed with WS_TreasureNames enum * EX: treasure.obtained[TREASURE_A] */ - TreasureNames obtained[MAX_TREASURE_COUNT]; -} Treasure; + WS_TreasureNames obtained[WS_MAX_TREASURE_COUNT]; +} WS_Treasure; // Custom save file structure for useful data typedef struct @@ -205,12 +222,12 @@ typedef struct /** * Array of level completion rates */ - Level levels[LEVELS_COUNT]; + WS_Level levels[WS_LEVELS_COUNT]; /** - * Treasure data + * WS_Treasure data */ - Treasure treasure; -} PlayerSave; + WS_Treasure treasure; +} WS_PlayerSave; typedef enum { @@ -218,7 +235,7 @@ typedef enum FILE_ERROR, MEMORY_ERROR, READ_ERROR -} FileError; +} WS_FileError; typedef enum { @@ -226,6 +243,6 @@ typedef enum FILE_B, FILE_C, FILE_COUNT -} SaveSlot; +} WS_SaveSlot; #endif diff --git a/lib/include/wariosave/helpers.h b/lib/include/wariosave/helpers.h index 5960d4f..b16daa2 100644 --- a/lib/include/wariosave/helpers.h +++ b/lib/include/wariosave/helpers.h @@ -4,8 +4,8 @@ * (See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) */ -#ifndef HELPERS_H -#define HELPERS_H +#ifndef WS_HELPERS_H +#define WS_HELPERS_H #include @@ -15,7 +15,7 @@ * @return The decimal value of the byte * @details Some bytes are stored in dec format in memory instead of hex. So we have to convert the literal value to int */ -int get_decimal_byte(int byte); +uint8_t ws_get_decimal_byte(const uint8_t byte); /** * Get the stored decimal value of 3 bytes @@ -26,70 +26,70 @@ int get_decimal_byte(int byte); * @details Some bytes are stored in dec format in memory instead of hex. So we have to convert the literal value to int */ -uint32_t get_decimal_bytes(int byte1, int byte2, int byte3); +uint32_t ws_get_decimal_bytes(const uint8_t byte1, const uint8_t byte2, const uint8_t byte3); /* * Load the save data into a buffer -* @param save The WarioSave buffer to load the save data into +* @param save The WS_WarioSave buffer to load the save data into * @param file_path The path to the save file -* @return The FileError error code +* @return The WS_FileError error code */ -int load_save_to_buffer(WarioGameSave *save, const char *file_path); +WS_FileError ws_load_save_to_buffer(WS_WarioGameSave *save, const char *file_path); /** - * Get the completion rate of each level saving the data into the PlayerSave struct - * @param save The WarioSave struct - * @param player_save The PlayerSave struct + * Get the completion rate of each level saving the data into the WS_PlayerSave struct + * @param save The WS_WarioSave struct + * @param player_save The WS_PlayerSave struct */ -Level *get_completion_rate(WarioSave *save, PlayerSave *player_save); +WS_Level *get_completion_rate(const WS_WarioSave *save, WS_PlayerSave *player_save); /** - * Initialize the PlayerSave struct with the data from the WarioSave struct - * @param save The WarioSave struct - * @param player_save The PlayerSave struct + * Initialize the WS_PlayerSave struct with the data from the WS_WarioSave struct + * @param save The WS_WarioSave struct + * @param player_save The WS_PlayerSave struct */ -void initialize_player_save(WarioSave *save, PlayerSave *player_save); +void ws_initialize_player_save(const WS_WarioSave *save, WS_PlayerSave *player_save); /** * Get the completion rate of each level - * @param save The WarioSave struct - * @param level_data The array of Level structs + * @param save The WS_WarioSave struct + * @param level_data The array of WS_Level structs */ -void get_level_completion_rates(WarioSave *save, Level *level_data); +void ws_get_level_completion_rates(const WS_WarioSave *save, WS_Level *level_data); /** * Get the player's lives - * @param save The WarioSave struct + * @param save The WS_WarioSave struct * @return The player's life count */ -int get_player_lives(WarioSave *save); +uint8_t ws_get_player_lives(const WS_WarioSave *save); /** * Get the player's hearts - * @param save The WarioSave struct + * @param save The WS_WarioSave struct * @return The player's heart count */ -int get_player_hearts(WarioSave *save); +uint8_t ws_get_player_hearts(const WS_WarioSave *save); /** * Get the player's coins - * @param save The WarioSave struct + * @param save The WS_WarioSave struct * @return The player's coin count */ -int get_player_coins(WarioSave *save); +uint32_t ws_get_player_coins(const WS_WarioSave *save); /** * Get the player's game completion status - * @param save The WarioSave struct + * @param save The WS_WarioSave struct * @return completed flag */ -bool get_is_game_completed(WarioSave *save); +bool ws_is_game_completed(const WS_WarioSave *save); /** * Get the save slot treasure data - * @param save The WarioSave struct - * @return Treasure struct + * @param save The WS_WarioSave struct + * @return WS_Treasure struct */ -Treasure get_treasure_data(WarioSave *save); +WS_Treasure ws_get_treasure_data(const WS_WarioSave *save); -#endif // HELPERS_H +#endif // WS_HELPERS_H diff --git a/lib/makefile b/lib/makefile index 5275e62..662edc1 100644 --- a/lib/makefile +++ b/lib/makefile @@ -15,7 +15,7 @@ SRCDIR = src BUILDDIR = build RELEASEDIR = $(BUILDDIR)/release -VERSION = 0.1.0 +VERSION = 0.2.0 # Source files SOURCES = $(wildcard $(SRCDIR)/*.c) diff --git a/lib/src/wariosave.c b/lib/src/wariosave.c index 811c68f..d8c968a 100644 --- a/lib/src/wariosave.c +++ b/lib/src/wariosave.c @@ -6,37 +6,35 @@ #include -#define BUFFER_PADDING 0x14 - -int get_decimal_byte(int byte) +uint8_t ws_get_decimal_byte(const uint8_t byte) { - char buffer[3]; + char buffer[4] = {"\0"}; snprintf(buffer, sizeof(buffer), "%02x", byte); - return (int)strtol(buffer, NULL, 10); + return (uint8_t)strtol(buffer, NULL, 10); } -uint32_t get_decimal_bytes(int byte1, int byte2, int byte3) +uint32_t ws_get_decimal_bytes(const uint8_t byte1, const uint8_t byte2, const uint8_t byte3) { - char buffer[MAX_COIN_DIGITS_STR]; + char buffer[WS_MAX_COIN_DIGITS_STR]; // Format the string into the buffer // Each byte is already in dec format. Use %x // so that the values are not converted "from hex to dec" - snprintf(buffer, sizeof(buffer), "%x%02x%02x", byte1, byte2, byte3); + snprintf(buffer, sizeof(buffer), "%02x%02x%02x", byte1, byte2, byte3); return (uint32_t)strtol(buffer, NULL, 10); } -int load_save_to_buffer(WarioGameSave *save, const char *file_path) +WS_FileError ws_load_save_to_buffer(WS_WarioGameSave *save, const char *file_path) { FILE *file = fopen(file_path, "rb"); - FileError error = NO_ERROR; + WS_FileError error = NO_ERROR; if (file == NULL) { perror("Error opening file"); return FILE_ERROR; } - for (int fileSlot = FILE_A, offset = SAVE_FILE_A_OFFSET; fileSlot < FILE_COUNT; fileSlot++, offset += SAVE_FILE_OFFSET) + for (int fileSlot = FILE_A, offset = WS_SAVE_FILE_A_OFFSET; fileSlot < FILE_COUNT; fileSlot++, offset += WS_SAVE_FILE_OFFSET) { if (fseek(file, offset, SEEK_SET) != 0) { @@ -45,19 +43,19 @@ int load_save_to_buffer(WarioGameSave *save, const char *file_path) return MEMORY_ERROR; } - uint8_t byte_array[SAVE_STRUCT_SIZE]; - size_t bytes_read = fread(byte_array, 1, SAVE_STRUCT_SIZE, file); + uint8_t byte_array[WS_SAVE_STRUCT_SIZE]; + size_t bytes_read = fread(byte_array, 1, WS_SAVE_STRUCT_SIZE, file); // Check if the read operation was successful - if (bytes_read != SAVE_STRUCT_SIZE) + if (bytes_read != WS_SAVE_STRUCT_SIZE) { perror("Error reading from file"); fclose(file); return READ_ERROR; } - // WarioSave struct is modeled after the byte array - save->save[fileSlot] = *(WarioSave *)byte_array; + // WS_WarioSave struct is modeled after the byte array + save->save[fileSlot] = *(WS_WarioSave *)byte_array; } // Close the file @@ -66,9 +64,9 @@ int load_save_to_buffer(WarioGameSave *save, const char *file_path) return error; } -void get_level_completion_rates(WarioSave *save, Level *level_data) +void ws_get_level_completion_rates(const WS_WarioSave *save, WS_Level *level_data) { - uint8_t level_completion_bitmask[LEVELS_COUNT] = { + uint8_t const level_completion_bitmask[WS_LEVELS_COUNT] = { save->sMapRiceBeachCompletion, save->sMapMtTeapotCompletion, save->sMapStoveCanyonCompletion, @@ -76,10 +74,10 @@ void get_level_completion_rates(WarioSave *save, Level *level_data) save->sMapParsleyWoodsCompletion, save->sMapSherbetLandCompletion, save->sMapSyrupCastleCompletion}; - for (int i = 0; i < LEVELS_COUNT; i++) + for (int i = 0; i < WS_LEVELS_COUNT; i++) { int count = 0; - uint8_t course_exit_count = default_level_data[i].course_exit_count; + uint8_t course_exit_count = WS_default_level_data[i].course_exit_count; level_data[i].completion_bitmask = level_completion_bitmask[i]; for (int j = 0; j < course_exit_count; j++) { @@ -89,54 +87,56 @@ void get_level_completion_rates(WarioSave *save, Level *level_data) } } -int get_player_lives(WarioSave *save) +uint8_t ws_get_player_lives(const WS_WarioSave *save) { - return get_decimal_byte(save->sLives); + return ws_get_decimal_byte(save->sLives); } -int get_player_hearts(WarioSave *save) +uint8_t ws_get_player_hearts(const WS_WarioSave *save) { - return get_decimal_byte(save->sHearts); + return ws_get_decimal_byte(save->sHearts); } -int get_player_coins(WarioSave *save) +uint32_t ws_get_player_coins(const WS_WarioSave *save) { - return get_decimal_bytes(save->sTotalCoins_High, save->sTotalCoins_Mid, save->sTotalCoins_Low); + return ws_get_decimal_bytes(save->sTotalCoins_High, save->sTotalCoins_Mid, save->sTotalCoins_Low); } -bool get_is_game_completed(WarioSave *save) +bool ws_is_game_completed(const WS_WarioSave *save) { return (bool)save->sGameCompleted; } -Treasure get_treasure_data(WarioSave *save) +WS_Treasure ws_get_treasure_data(const WS_WarioSave *save) { - Treasure treasure; - treasure.count = 0; - treasure.completion_rate = 0; - for (int i = 0; i < MAX_TREASURE_COUNT; i++) + WS_Treasure treasure = { + .obtained = {0}, + .count = 0, + .completion_rate = 0 + }; + + for (int i = 0; i < WS_MAX_TREASURE_COUNT; i++) { - treasure.obtained[i] = 0; - // first byte in sTreasures is unused - if ((save->sTreasures >> (i+1)) & 1) + // initial LSB in sTreasures is unused + if ((save->sTreasures >> (i + 1)) & 1) { - treasure.obtained[i] = 1; + treasure.obtained[i] = true; treasure.count++; } } - treasure.completion_rate = (int)(treasure.count / (MAX_TREASURE_COUNT * 1.0) * 100); + treasure.completion_rate = (int)(treasure.count / (WS_MAX_TREASURE_COUNT * 1.0) * 100); return treasure; } -void initialize_player_save(WarioSave *save, PlayerSave *player_save) +void ws_initialize_player_save(const WS_WarioSave *save, WS_PlayerSave *player_save) { - player_save->lives = get_player_lives(save); - player_save->hearts = get_player_hearts(save); - player_save->total_coins = get_player_coins(save); - player_save->game_completed = get_is_game_completed(save); - player_save->treasure = get_treasure_data(save); - Level player_level_data[LEVELS_COUNT]; - get_level_completion_rates(save, player_level_data); + player_save->lives = ws_get_player_lives(save); + player_save->hearts = ws_get_player_hearts(save); + player_save->total_coins = ws_get_player_coins(save); + player_save->game_completed = ws_is_game_completed(save); + player_save->treasure = ws_get_treasure_data(save); + WS_Level player_level_data[WS_LEVELS_COUNT]; + ws_get_level_completion_rates(save, player_level_data); memcpy(player_save->levels, &player_level_data, sizeof(player_save->levels)); } diff --git a/test/makefile b/test/makefile new file mode 100644 index 0000000..0d51a9a --- /dev/null +++ b/test/makefile @@ -0,0 +1,20 @@ +# Copyright (c) 2024 github/savaughn +# Distributed under the MIT License (MIT) +# See accompanying file LICENSE.txt or copy at http://opensource.org/licenses/MIT) + +CC = gcc +CFLAGS = -Wall -I../lib/include -I/opt/homebrew/Cellar/cunit/2.1-3/include +LDFLAGS = -L/opt/homebrew/Cellar/cunit/2.1-3/lib -L../lib/build -lcunit -lwariosave +TARGET = wariosave_test +SRC = $(wildcard src/*.c) + +all: $(TARGET) + +$(TARGET): $(SRC) + @$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test: $(TARGET) + @./$(TARGET) + +clean: + @rm -f $(TARGET) \ No newline at end of file diff --git a/test/saves/wario.sav b/test/saves/wario.sav new file mode 100644 index 0000000000000000000000000000000000000000..123b053ee9798f65117935aa31c5cb53bcf5b870 GIT binary patch literal 8192 zcmeHM+fy4=7(bf?0wGAaLqT&0X`vS(lm<#$lLZ=}wiepj(3?WdrqmP~3P>qJc55H% zjE?n*;qt;b<4k9Cs{cR-e9#x2@u4$}z9`P1J~;#48atTve7o6XbJ@sc>`Z z-*-9Rp0l|XGYy@ER7x|wlVbUX0M8ojycS^!1*v!qb}hl4R)<_9S78SKwb-|768kIJ z@ATQ?J4RHYEU4}xnnJ(+`6#t}_LF)Q%0O)gf@n(jK-usAVtLxvPwt}z@nlTRjk?`a z2pP=Lx4FWJD3w~160KjGT*QTNSs@M>g~JnpA;J)pDOW<_QBq=L!$3T0uMM@3E3dWA)yk`2T#MZTD!??QhTv&+V{{Eit{3u^Vk@+FZ-)Fh` z#pPwcf9!tOTxWIR?);#h@2$%%xy>KU)KO^OZ~vN|>)b^i0*it7mTYPW%G!-uzBp6h zW0I^#F%|}^krrtw3OU27HF57Sh7@>dsdq#5TogP*VniG*k(A!QBY;~|P@_#e@~Hi& z8x_q=Q0QaE!r^pGb)(93oW4L}au}h9=|9GK$nKF+lIZBR#@$6lceFi-Luz%F-k58z zVtp)29;Jc#`DKg0ZT0s+AmHOcBYNE8$HtKnQHVG2l|O&6LQSHdNXEB?tOkR{GIDur zh-Fxl z9(HtiYrRmvmoRnpEv-$>Cr^339kos??WpT)X>78-?5JyQIep1J%(#1)>%+`2!(1m9 zAy}@bcXX`p%J`cv9zAgI*h`0A@bvE5-FIcr^JmUpeqFpqZk#zga--_#x%0=WFTB!S zbJ$_INVo2@zILjq{-n#(yC3`;pvMvw6>lqnkDO`gx(sOs9$pegKF`SrZjamsygu`p zESOE@rgB`?b_94iRqhloLv~JXUVcF#tV@amyc`R>ye9CG>H;5|eeYGkn99XGuXc&C zBpGZzA1*VSaT)OJ4t59Zp$c-btf*^eeeLY=$`N*zP}r5L!XC2$drGshCocyNm@ytGEyDwOCOm4k;L*|=Jes!` zZ+PQ9ZpS6dJ?od&Lk`^obt<1}(f!i+N28J6Msrkl;}QCvX_i`Q`doGz9(R4oe8~^P_m_G$9`PLu& zF(D*HFd&2*-cSCiP$D6ml5vgt{&=|m?1uy@!D72jjTnw9Rvyn!9y5`kfdY> tp6Cul?fi|bCGxb05#s4K5 +#include + +void test_get_decimal_byte(void) { + CU_ASSERT_EQUAL(ws_get_decimal_byte(0x0), 0); + CU_ASSERT_EQUAL(ws_get_decimal_byte(0x99), 99); +} + +void test_get_decimal_bytes(void) { + CU_ASSERT_EQUAL(ws_get_decimal_bytes(0x00, 0x00, 0x00), 0); + CU_ASSERT_EQUAL(ws_get_decimal_bytes(0x01, 0x50, 0x43), 15043); + CU_ASSERT_EQUAL(ws_get_decimal_bytes(0x99, 0x99, 0x99), 999999); +} + +void test_load_save_to_buffer(void){ + WS_WarioGameSave game_save; + WS_WarioSave save_file; + const char *file_path = "saves/wario.sav"; + + WS_FileError error = ws_load_save_to_buffer(&game_save, file_path); + CU_ASSERT_EQUAL(error, NO_ERROR); + save_file = game_save.save[FILE_A]; + + CU_ASSERT_EQUAL(save_file.sGameCompleted, 1); +} + +void test_get_completion_rate(void) { + WS_WarioSave save = { + .sMapRiceBeachCompletion = 0b01111111, + .sMapMtTeapotCompletion = 0b01111111, + .sMapStoveCanyonCompletion = 0b00111111, + .sMapSSTeacupCompletion = 0b00011111, + .sMapParsleyWoodsCompletion = 0b00111111, + .sMapSherbetLandCompletion = 0b00111111, + .sMapSyrupCastleCompletion = 0b00000111 + }; + WS_Level level_data[WS_LEVELS_COUNT]; + ws_get_level_completion_rates(&save, level_data); + for (int i = 0; i < WS_LEVELS_COUNT; i++) { + CU_ASSERT_EQUAL(level_data[i].completion_rate, 100); + } +} + +void test_get_player_lives(void) { + WS_WarioSave save = {.sLives = 5}; + CU_ASSERT_EQUAL(ws_get_player_lives(&save), 5); +} + +void test_get_player_hearts(void) { + WS_WarioSave save = {.sHearts = 3}; + CU_ASSERT_EQUAL(ws_get_player_hearts(&save), 3); +} + +void test_get_player_coins(void) { + WS_WarioSave save = {.sTotalCoins_High = 0x01, .sTotalCoins_Mid = 0x50, .sTotalCoins_Low = 0x43}; + CU_ASSERT_EQUAL(ws_get_player_coins(&save), 15043); +} + +void test_is_game_completed(void) { + WS_WarioSave save = {.sGameCompleted = 1}; + CU_ASSERT_EQUAL(ws_is_game_completed(&save), true); +} + +void test_get_treasure_data(void) { + WS_WarioSave save = {.sTreasures = 0b1111111111111110}; + WS_Treasure treasure = ws_get_treasure_data(&save); + CU_ASSERT_EQUAL(treasure.completion_rate, 100); +} + +int main(void) { + CU_pSuite suite = NULL; + + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + suite = CU_add_suite("helpers_test_suite", 0, 0); + if (NULL == suite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ((NULL == CU_add_test(suite, "test_get_decimal_byte", test_get_decimal_byte)) || + (NULL == CU_add_test(suite, "test_get_decimal_bytes", test_get_decimal_bytes)) || + (NULL == CU_add_test(suite, "test_load_save_to_buffer", test_load_save_to_buffer)) || + (NULL == CU_add_test(suite, "test_get_player_lives", test_get_player_lives)) || + (NULL == CU_add_test(suite, "test_get_player_hearts", test_get_player_hearts)) || + (NULL == CU_add_test(suite, "test_get_player_coins", test_get_player_coins)) || + (NULL == CU_add_test(suite, "test_get_completion_rate", test_get_completion_rate)) || + (NULL == CU_add_test(suite, "test_is_game_completed", test_is_game_completed)) || + (NULL == CU_add_test(suite, "test_get_treasure_data", test_get_treasure_data))) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + return CU_get_error(); +} \ No newline at end of file