Make Chips, Mult and Score unsigned and protect against overflow (#212)

---------

Co-authored-by: MathisMartin31 <mathis.martin31@gmail.com>
This commit is contained in:
Geralt
2025-11-20 12:39:15 +02:00
committed by GitHub
co-authored by MathisMartin31
parent 47b1bac31f
commit 2fb41e533c
6 changed files with 74 additions and 38 deletions
+4 -4
View File
@@ -97,9 +97,9 @@ typedef struct JokerObject
typedef struct // These jokers are triggered after the played hand has finished scoring. typedef struct // These jokers are triggered after the played hand has finished scoring.
{ {
int chips; u32 chips;
int mult; u32 mult;
int xmult; u32 xmult;
int money; 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 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) 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_destroy(JokerObject **joker_object);
void joker_object_update(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 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); void joker_object_set_selected(JokerObject* joker_object, bool selected);
bool joker_object_is_selected(JokerObject* joker_object); bool joker_object_is_selected(JokerObject* joker_object);
+10 -1
View File
@@ -1,6 +1,8 @@
#ifndef UTIL_H #ifndef UTIL_H
#define UTIL_H #define UTIL_H
#include <stdint.h>
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 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; 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 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); int int_arr_max(int int_arr[], int size);
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 #endif // UTIL_H
+22 -24
View File
@@ -62,8 +62,8 @@ typedef enum
typedef struct typedef struct
{ {
int chips; u32 chips;
int mult; u32 mult;
char *display_name; char *display_name;
} HandValues; } HandValues;
@@ -205,14 +205,14 @@ static int discards = 0;
static int round = 0; static int round = 0;
static int ante = 0; static int ante = 0;
static int money = 0; static int money = 0;
static int score = 0; static u32 score = 0;
static int temp_score = 0; // This is the score that shows in the same spot as the hand type. 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 bool score_flames_active = false;
static FIXED lerped_score = 0; static FIXED lerped_score = 0;
static FIXED lerped_temp_score = 0; static FIXED lerped_temp_score = 0;
static int chips = 0; static u32 chips = 0;
static int mult = 0; static u32 mult = 0;
static bool retrigger = false; static bool retrigger = false;
static int hand_size = 8; // Default hand size is 8 static int hand_size = 8; // Default hand size is 8
@@ -1091,20 +1091,20 @@ void change_background(int id)
background = 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; int x_offset = 40 - get_digits_even(value) * TILE_SIZE;
tte_erase_rect_wrapper(TEMP_SCORE_RECT); 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 // Clear the existing text before redrawing
tte_erase_rect_wrapper(SCORE_RECT); tte_erase_rect_wrapper(SCORE_RECT);
char score_suffix = ' '; char score_suffix = ' ';
int display_value = value; u32 display_value = value;
if(value >= TEN_K) if(value >= TEN_K)
{ {
@@ -1124,7 +1124,7 @@ void display_score(int value)
int rect_width = SCORE_RECT.right - SCORE_RECT.left; int rect_width = SCORE_RECT.right - SCORE_RECT.left;
int x_offset = SCORE_RECT.left + (rect_width - text_width) / 2; 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) void display_money(int value)
@@ -1164,14 +1164,14 @@ void display_chips()
Rect chips_text_rect = CHIPS_TEXT_RECT; Rect chips_text_rect = CHIPS_TEXT_RECT;
tte_erase_rect_wrapper(CHIPS_TEXT_RECT); tte_erase_rect_wrapper(CHIPS_TEXT_RECT);
update_text_rect_to_right_align_num(&chips_text_rect, chips, OVERFLOW_LEFT); 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(); check_flaming_score();
} }
void display_mult() void display_mult()
{ {
tte_erase_rect_wrapper(MULT_TEXT_RECT); 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(); check_flaming_score();
} }
@@ -1658,7 +1658,8 @@ static void game_playing_process_input_and_state()
{ {
if (mult > 0) 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_temp_score = int2fx(temp_score);
lerped_score = int2fx(score); lerped_score = int2fx(score);
@@ -1682,19 +1683,14 @@ static void game_playing_process_input_and_state()
if (lerped_temp_score > 0) 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 // We actually don't need to erase this because the score only increases
display_score(fx2int(lerped_score)); // Set the score display display_score(fx2uint(lerped_score)); // Set the score display
if (temp_score <= 0)
{
tte_erase_rect_wrapper(TEMP_SCORE_RECT);
}
} }
else else
{ {
score += temp_score; score = u32_protected_add(score, temp_score);
temp_score = 0; temp_score = 0;
lerped_temp_score = 0; lerped_temp_score = 0;
lerped_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_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 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 // Write the score to a character buffer variable
char score_buffer[INT_MAX_DIGITS + 2]; // for '+' and null terminator 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); tte_write(score_buffer);
card_object_shake(scored_card_object, SFX_CARD_SELECT); card_object_shake(scored_card_object, SFX_CARD_SELECT);
// Relocated card scoring logic here // Relocated card scoring logic here
chips += card_get_value(scored_card_object->card); chips = u32_protected_add(chips, card_value);
display_chips(); display_chips();
// Allow Joker scoring // Allow Joker scoring
+13 -7
View File
@@ -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; *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) if (joker_object == NULL)
{ {
@@ -277,9 +277,15 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum
return false; return false;
} }
*chips += joker_effect.chips; // protect chips and mult against overflow
*mult += joker_effect.mult; *chips = u32_protected_add(*chips, joker_effect.chips);
*mult *= joker_effect.xmult > 0 ? joker_effect.xmult : 1; // if xmult is zero, DO NOT multiply by it *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; *money += joker_effect.money;
*retrigger = joker_effect.retrigger; *retrigger = joker_effect.retrigger;
// joker_effect.message will have been set if the Joker had anything custom to say // 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) if (joker_effect.chips > 0)
{ {
char score_buffer[INT_MAX_DIGITS + 2]; // For '+' and null terminator 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); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_BLUE_PB);
} }
if (joker_effect.mult > 0) if (joker_effect.mult > 0)
{ {
char score_buffer[INT_MAX_DIGITS + 2]; 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); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
} }
if (joker_effect.xmult > 0) if (joker_effect.xmult > 0)
{ {
char score_buffer[INT_MAX_DIGITS + 2]; 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); set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
} }
if (joker_effect.money > 0) if (joker_effect.money > 0)
+1 -1
View File
@@ -745,7 +745,7 @@ static JokerEffect shoot_the_moon_joker_effect(Joker *joker, Card *scored_card,
if (scored_card->rank == QUEEN) if (scored_card->rank == QUEEN)
{ {
effect.mult += 13; effect.mult = u32_protected_add(effect.mult, 13);
} }
return effect; return effect;
+23
View File
@@ -14,3 +14,26 @@ int int_arr_max(int int_arr[], int size)
return max; 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);
}