GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
util.c
1#include "util.h"
2
3#include "font.h"
4
5#include <limits.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <string.h>
9
10int int_arr_max(int int_arr[], int size)
11{
12 int max = INT_MIN;
13 for (int i = 0; i < size; i++)
14 {
15 if (int_arr[i] > max)
16 {
17 max = int_arr[i];
18 }
19 }
20
21 return max;
22}
23
34static inline void num_str_truncate_trailing_zeros(char* num_str, int size)
35{
36 while (size > 0 && num_str[size - 1] == '0')
37 {
38 size--;
39 }
40 num_str[size] = '\0';
41}
42
61static inline void truncate_num_get_remainder_string(
62 uint32_t decimal_remainder,
63 uint32_t truncated_num,
64 int num_req_chars,
65 char suffix_char,
66 char remainder_str[UINT_MAX_DIGITS + 1]
67)
68{
69 // Truncating the remainder in string form rather than number to avoid divisions
70 char* remainder_str_format;
71
72 switch (suffix_char)
73 {
74 // Pad with 0s to not lose leading zeros after decimal point
75 case 'B':
76 remainder_str_format = "%09lu";
77 break;
78 case 'M':
79 remainder_str_format = "%06lu";
80 break;
81 case 'K':
82 remainder_str_format = "%03lu";
83 break;
84 default:
85 // Should not reach here
86 remainder_str_format = "%lu";
87 }
88
89 snprintf(remainder_str, UINT_MAX_DIGITS + 1, remainder_str_format, decimal_remainder);
90
91 // Truncate overflow
92 int remaining_chars = num_req_chars - u32_get_digits(truncated_num) - 1; // - 1 for suffix
93
94 // If there is no room for any fractional characters, leave the remainder string empty.
95 if (remaining_chars <= 0)
96 {
97 remainder_str[0] = '\0';
98 return;
99 }
100
101 // Ensure we never write past the end of the buffer.
102 if (remaining_chars > UINT_MAX_DIGITS)
103 {
104 remaining_chars = UINT_MAX_DIGITS;
105 }
106 remainder_str[remaining_chars] = '\0';
107
108 num_str_truncate_trailing_zeros(remainder_str, remaining_chars);
109
110 if (remainder_str[0] != '\0')
111 {
112 remainder_str[0] = digit_char_to_font_point(remainder_str[0]);
113 }
114}
115
117 uint32_t num,
118 int num_req_chars,
119 char out_str_buff[UINT_MAX_DIGITS + 1]
120)
121{
122 uint32_t truncated_num = num;
123 int num_digits = u32_get_digits(num);
124 uint32_t decimal_remainder = 0;
125 bool overflow = num_digits > num_req_chars;
126 char* suffix = "";
127 char remainder_str[UINT_MAX_DIGITS + 1];
128 remainder_str[0] = '\0';
129
130 if (overflow)
131 {
132 /* If there is overflow, divide by the next suffixed power of 10
133 * to truncate the number back within num_req_chars.
134 * UINT32_MAX is in the billions so no need to check larger numbers
135 * or perform complex mathematical operations.
136 */
137 uint32_t divisor = 1;
138 if (num >= ONE_B)
139 {
140 divisor = ONE_B;
141 suffix = "B";
142 }
143 else if (num >= ONE_M)
144 {
145 divisor = ONE_M;
146 suffix = "M";
147 }
148 else if (num >= ONE_K)
149 {
150 divisor = ONE_K;
151 suffix = "K";
152 }
153
154 // The compiler optimizes these into a single operation
155 truncated_num = num / divisor;
156 decimal_remainder = num % divisor;
157 }
158
159 if (suffix[0] != '\0' && decimal_remainder != 0)
160 {
161 truncate_num_get_remainder_string(
162 decimal_remainder,
163 truncated_num,
164 num_req_chars,
165 suffix[0],
166 remainder_str
167 );
168 }
169
170 snprintf(out_str_buff, UINT_MAX_DIGITS + 1, "%lu%s%s", truncated_num, remainder_str, suffix);
171}
172
173// Avoid uint overflow when add/multiplying score
174
175uint32_t u32_protected_add(uint32_t a, uint32_t b)
176{
177 return (a > (UINT32_MAX - b)) ? UINT32_MAX : (a + b);
178}
179
180uint16_t u16_protected_add(uint16_t a, uint16_t b)
181{
182 return (a > (UINT16_MAX - b)) ? UINT16_MAX : (a + b);
183}
184
185uint32_t u32_protected_mult(uint32_t a, uint32_t b)
186{
187 return (a == 0 || b == 0) ? 0 : (a > (UINT32_MAX / b) ? UINT32_MAX : a * b);
188}
189
190uint16_t u16_protected_mult(uint16_t a, uint16_t b)
191{
192 return (a == 0 || b == 0) ? 0 : (a > (UINT16_MAX / b) ? UINT16_MAX : a * b);
193}
194
204static inline uint32_t base36_digit_value(char c)
205{
206 switch (c)
207 {
208 case '0' ... '9':
209 return c - '0';
210 case 'A' ... 'Z':
211 return 10 + c - 'A';
212 case 'a' ... 'z':
213 return 10 + c - 'a';
214 default:
215 return 0;
216 }
217}
218
228static inline char base36_digit_char(uint32_t n)
229{
230 switch (n)
231 {
232 case 0 ... 9:
233 return n + '0';
234 case 10 ... 35:
235 return n - 10 + 'A';
236 default:
237 return '\0';
238 }
239}
240
249static inline uint32_t get_base36_power(uint8_t i)
250{
251 if (i >= BASE36_MAX_DIGITS)
252 {
253 return 0;
254 }
255
256 static const uint32_t powers_of_36[BASE36_MAX_DIGITS] = {1, 36, 1296, 46656, 1679616, 60466176};
257 return powers_of_36[i];
258}
259
260uint32_t base36_to_u32(const char b36_str[])
261{
262 uint32_t res = 0;
263
264 for (uint8_t i = 0; i < BASE36_MAX_DIGITS; i++)
265 {
266 res += base36_digit_value(b36_str[i]) * get_base36_power(BASE36_MAX_DIGITS - i - 1);
267 }
268
269 return res;
270}
271
272void u32_to_base36(const uint32_t n, char b36_str[])
273{
274 uint32_t power;
275 uint32_t acc = (n > MAX_BASE36) ? MAX_BASE36 : n;
276 for (int i = 0; i < BASE36_MAX_DIGITS; i++)
277 {
278 power = get_base36_power(BASE36_MAX_DIGITS - i - 1);
279 b36_str[i] = base36_digit_char(acc / power);
280 acc = acc % power;
281 }
282 // Properly end the string
283 b36_str[BASE36_MAX_DIGITS] = '\0';
284}
Utility file to interact with custom font.
char digit_char_to_font_point(char digit_char)
Get the decimal point char equivalent of 0-9 from char param.
Definition font.c:26
Utilities relating around number string representation and protected arithmatic helper functions.
uint16_t u16_protected_mult(uint16_t a, uint16_t b)
Avoid overflow when multiplying two u16 integers.
Definition util.c:190
uint32_t u32_protected_add(uint32_t a, uint32_t b)
Avoid overflow when adding two u32 integers.
Definition util.c:175
#define MAX_BASE36
Hex value of "ZZZZZZ" in base 36.
Definition util.h:27
static int u32_get_digits(uint32_t n)
Get the number of digits in a 32-bit unsigned number https://stackoverflow.com/questions/1068849/how-...
Definition util.h:183
uint32_t base36_to_u32(const char b36_str[])
Convert a base-36 string representation to a 32-bit unsigned integer. Since we are dealing with base-...
Definition util.c:260
uint16_t u16_protected_add(uint16_t a, uint16_t b)
Avoid overflow when adding two u16 integers.
Definition util.c:180
void truncate_uint_to_suffixed_str(uint32_t num, int num_req_chars, char out_str_buff[UINT_MAX_DIGITS+1])
Truncate an unsigned number into a suffixed string representation e.g. 12000 -> "12K" The least signi...
Definition util.c:116
uint32_t u32_protected_mult(uint32_t a, uint32_t b)
Avoid overflow when multiplying two u32 integers.
Definition util.c:185
void u32_to_base36(uint32_t n, char b36_str[])
Convert a 32-bit unsigned integer to its base-36 string representation. This will perform 6 divisions...
Definition util.c:272