From 2fb41e533c9c4b473ddf1c1272d3b466460207e2 Mon Sep 17 00:00:00 2001 From: Geralt <84459734+MathisMartin31@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:39:15 +0100 Subject: [PATCH] Make Chips, Mult and Score unsigned and protect against overflow (#212) --------- Co-authored-by: MathisMartin31 --- include/joker.h | 8 ++++---- include/util.h | 13 ++++++++++-- source/game.c | 46 ++++++++++++++++++++---------------------- source/joker.c | 20 +++++++++++------- source/joker_effects.c | 2 +- source/util.c | 23 +++++++++++++++++++++ 6 files changed, 74 insertions(+), 38 deletions(-) diff --git a/include/joker.h b/include/joker.h index b2894b4..f4ac70f 100644 --- a/include/joker.h +++ b/include/joker.h @@ -97,9 +97,9 @@ typedef struct JokerObject typedef struct // These jokers are triggered after the played hand has finished scoring. { - int chips; - int mult; - int xmult; + u32 chips; + u32 mult; + u32 xmult; int money; bool retrigger; // Retrigger played hand (e.g. "Dusk" joker, even though on the wiki it says "On Scored" it makes more sense to have it here) bool expire; // Joker is destroyed (food jokers) @@ -129,7 +129,7 @@ JokerObject *joker_object_new(Joker *joker); void joker_object_destroy(JokerObject **joker_object); void joker_object_update(JokerObject *joker_object); void joker_object_shake(JokerObject *joker_object, mm_word sound_id); // This doesn't actually score anything, it just performs an animation and plays a sound effect -bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event, int *chips, int *mult, int *money, bool *retrigger); // This scores the joker and returns true if it was scored successfully (Card = NULL means the joker is independent and not scored by a card) +bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event, u32 *chips, u32 *mult, int *money, bool *retrigger); // This scores the joker and returns true if it was scored successfully (Card = NULL means the joker is independent and not scored by a card) void joker_object_set_selected(JokerObject* joker_object, bool selected); bool joker_object_is_selected(JokerObject* joker_object); diff --git a/include/util.h b/include/util.h index 3899a68..6b2c819 100644 --- a/include/util.h +++ b/include/util.h @@ -1,6 +1,8 @@ #ifndef UTIL_H #define UTIL_H +#include + static inline int get_digits(int n) // https://stackoverflow.com/questions/1068849/how-do-i-determine-the-number-of-digits-of-an-integer-in-c { if (n < 10) return 1; @@ -37,8 +39,15 @@ static inline int get_digits_even(int n) #define NUM_ELEM_IN_ARR(arr) (sizeof(arr) / sizeof((arr)[0])) -#define INT_MAX_DIGITS 11 // strlen(str(INT_MAX)) = strlen("-2147483647") +#define INT_MAX_DIGITS 11 // strlen(str(INT_MAX)) = strlen("-2147483647") +#define UINT_MAX_DIGITS 10 // strlen(str(UINT32_MAX)) = strlen("4294967295") +#define UINT8_MAX_DIGITS 3 // strlen(str(UINT8_MAX)) = strlen("255") int int_arr_max(int int_arr[], int size); -#endif // UTIL_H \ No newline at end of file +uint32_t u32_protected_add (uint32_t a, uint32_t b); +uint16_t u16_protected_add (uint16_t a, uint16_t b); +uint32_t u32_protected_mult(uint32_t a, uint32_t b); +uint16_t u16_protected_mult(uint16_t a, uint16_t b); + +#endif // UTIL_H diff --git a/source/game.c b/source/game.c index bcfa6ae..4952c3b 100644 --- a/source/game.c +++ b/source/game.c @@ -62,8 +62,8 @@ typedef enum typedef struct { - int chips; - int mult; + u32 chips; + u32 mult; char *display_name; } HandValues; @@ -205,14 +205,14 @@ static int discards = 0; static int round = 0; static int ante = 0; static int money = 0; -static int score = 0; -static int temp_score = 0; // This is the score that shows in the same spot as the hand type. +static u32 score = 0; +static u32 temp_score = 0; // This is the score that shows in the same spot as the hand type. static bool score_flames_active = false; static FIXED lerped_score = 0; static FIXED lerped_temp_score = 0; -static int chips = 0; -static int mult = 0; +static u32 chips = 0; +static u32 mult = 0; static bool retrigger = false; static int hand_size = 8; // Default hand size is 8 @@ -1091,20 +1091,20 @@ void change_background(int id) background = id; } -void display_temp_score(int value) +void display_temp_score(u32 value) { int x_offset = 40 - get_digits_even(value) * TILE_SIZE; tte_erase_rect_wrapper(TEMP_SCORE_RECT); - tte_printf("#{P:%d,%d; cx:0x%X000}%d", x_offset, TEMP_SCORE_RECT.top, TTE_WHITE_PB, value); + tte_printf("#{P:%d,%d; cx:0x%X000}%lu", x_offset, TEMP_SCORE_RECT.top, TTE_WHITE_PB, value); } -void display_score(int value) +void display_score(u32 value) { // Clear the existing text before redrawing tte_erase_rect_wrapper(SCORE_RECT); char score_suffix = ' '; - int display_value = value; + u32 display_value = value; if(value >= TEN_K) { @@ -1124,7 +1124,7 @@ void display_score(int value) int rect_width = SCORE_RECT.right - SCORE_RECT.left; int x_offset = SCORE_RECT.left + (rect_width - text_width) / 2; - tte_printf("#{P:%d,48; cx:0x%X000}%d%c", x_offset, TTE_WHITE_PB, display_value, score_suffix); + tte_printf("#{P:%d,48; cx:0x%X000}%lu%c", x_offset, TTE_WHITE_PB, display_value, score_suffix); } void display_money(int value) @@ -1164,14 +1164,14 @@ void display_chips() Rect chips_text_rect = CHIPS_TEXT_RECT; tte_erase_rect_wrapper(CHIPS_TEXT_RECT); update_text_rect_to_right_align_num(&chips_text_rect, chips, OVERFLOW_LEFT); - tte_printf("#{P:%d,%d; cx:0x%X000;}%d", chips_text_rect.left, chips_text_rect.top, TTE_WHITE_PB, chips); + tte_printf("#{P:%d,%d; cx:0x%X000;}%lu", chips_text_rect.left, chips_text_rect.top, TTE_WHITE_PB, chips); check_flaming_score(); } void display_mult() { tte_erase_rect_wrapper(MULT_TEXT_RECT); - tte_printf("#{P:%d,%d; cx:0x%X000;}%d", MULT_TEXT_RECT.left, MULT_TEXT_RECT.top, TTE_WHITE_PB, mult); + tte_printf("#{P:%d,%d; cx:0x%X000;}%lu", MULT_TEXT_RECT.left, MULT_TEXT_RECT.top, TTE_WHITE_PB, mult); check_flaming_score(); } @@ -1658,7 +1658,8 @@ static void game_playing_process_input_and_state() { if (mult > 0) { - temp_score = chips * mult; + // protect against score overflow + temp_score = u32_protected_mult(chips, mult); lerped_temp_score = int2fx(temp_score); lerped_score = int2fx(score); @@ -1682,19 +1683,14 @@ static void game_playing_process_input_and_state() if (lerped_temp_score > 0) { - display_temp_score(fx2int(lerped_temp_score)); + display_temp_score(fx2uint(lerped_temp_score)); // We actually don't need to erase this because the score only increases - display_score(fx2int(lerped_score)); // Set the score display - - if (temp_score <= 0) - { - tte_erase_rect_wrapper(TEMP_SCORE_RECT); - } + display_score(fx2uint(lerped_score)); // Set the score display } else { - score += temp_score; + score = u32_protected_add(score, temp_score); temp_score = 0; lerped_temp_score = 0; lerped_score = 0; @@ -2277,15 +2273,17 @@ static bool play_scoring_cards_update() tte_set_pos(fx2int(scored_card_object->sprite_object->x) + TILE_SIZE, SCORED_CARD_TEXT_Y); // Offset of 1 tile to keep the text on the card tte_set_special(TTE_BLUE_PB * TTE_SPECIAL_PB_MULT_OFFSET); // Set text color to blue from background memory + u8 card_value = card_get_value(scored_card_object->card); + // Write the score to a character buffer variable char score_buffer[INT_MAX_DIGITS + 2]; // for '+' and null terminator - snprintf(score_buffer, sizeof(score_buffer), "+%d", card_get_value(scored_card_object->card)); + snprintf(score_buffer, sizeof(score_buffer), "+%hhu", card_value); tte_write(score_buffer); card_object_shake(scored_card_object, SFX_CARD_SELECT); // Relocated card scoring logic here - chips += card_get_value(scored_card_object->card); + chips = u32_protected_add(chips, card_value); display_chips(); // Allow Joker scoring diff --git a/source/joker.c b/source/joker.c index e60bcab..477f097 100644 --- a/source/joker.c +++ b/source/joker.c @@ -263,7 +263,7 @@ void set_and_shift_text(char* str, int* cursor_pos_x, int* cursor_pos_y, int col *cursor_pos_x += joker_score_display_offset_px; } -bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event, int *chips, int *mult, int *money, bool *retrigger) +bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event, u32 *chips, u32 *mult, int *money, bool *retrigger) { if (joker_object == NULL) { @@ -277,9 +277,15 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum return false; } - *chips += joker_effect.chips; - *mult += joker_effect.mult; - *mult *= joker_effect.xmult > 0 ? joker_effect.xmult : 1; // if xmult is zero, DO NOT multiply by it + // protect chips and mult against overflow + *chips = u32_protected_add(*chips, joker_effect.chips); + *mult = u32_protected_add(*mult, joker_effect.mult); + // If xMult is 0 DON'T multiply by it! + if (joker_effect.xmult > 0) + { + *mult = u32_protected_mult(*mult, joker_effect.xmult); + } + *money += joker_effect.money; *retrigger = joker_effect.retrigger; // joker_effect.message will have been set if the Joker had anything custom to say @@ -302,19 +308,19 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum if (joker_effect.chips > 0) { char score_buffer[INT_MAX_DIGITS + 2]; // For '+' and null terminator - snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); + snprintf(score_buffer, sizeof(score_buffer), "+%lu", joker_effect.chips); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_BLUE_PB); } if (joker_effect.mult > 0) { char score_buffer[INT_MAX_DIGITS + 2]; - snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); + snprintf(score_buffer, sizeof(score_buffer), "+%lu", joker_effect.mult); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB); } if (joker_effect.xmult > 0) { char score_buffer[INT_MAX_DIGITS + 2]; - snprintf(score_buffer, sizeof(score_buffer), "X%d", joker_effect.xmult); + snprintf(score_buffer, sizeof(score_buffer), "X%lu", joker_effect.xmult); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB); } if (joker_effect.money > 0) diff --git a/source/joker_effects.c b/source/joker_effects.c index 82053b6..fe0b7ee 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -745,7 +745,7 @@ static JokerEffect shoot_the_moon_joker_effect(Joker *joker, Card *scored_card, if (scored_card->rank == QUEEN) { - effect.mult += 13; + effect.mult = u32_protected_add(effect.mult, 13); } return effect; diff --git a/source/util.c b/source/util.c index 94af7ef..6e8afff 100644 --- a/source/util.c +++ b/source/util.c @@ -14,3 +14,26 @@ int int_arr_max(int int_arr[], int size) return max; } + +// Avoid uint overflow when add/multiplying score + +uint32_t u32_protected_add(uint32_t a, uint32_t b) +{ + return (a > (UINT32_MAX - b)) ? UINT32_MAX : (a + b); +} + +uint16_t u16_protected_add(uint16_t a, uint16_t b) +{ + return (a > (UINT16_MAX - b)) ? UINT16_MAX : (a + b); +} + +uint32_t u32_protected_mult(uint32_t a, uint32_t b) +{ + return (a == 0 || b == 0) ? 0 : (a > (UINT32_MAX / b) ? UINT32_MAX : a * b); +} + +uint16_t u16_protected_mult(uint16_t a, uint16_t b) +{ + return (a == 0 || b == 0) ? 0 : (a > (UINT16_MAX / b) ? UINT16_MAX : a * b); + +}