From 4e5a863269771f3f0ff9206d0d360a61ef861c7f Mon Sep 17 00:00:00 2001 From: Jamiras <32680403+Jamiras@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:45:59 -0700 Subject: [PATCH] Support for new value formats (#305) --- include/rc_runtime_types.h | 9 +++- src/rc_client.c | 7 +++ src/rcheevos/format.c | 74 +++++++++++++++++++++++++++++++ src/rcheevos/richpresence.c | 4 ++ test/rcheevos/test_format.c | 40 +++++++++++++++++ test/rcheevos/test_richpresence.c | 43 ++++++++++++++++++ test/test_rc_client.c | 27 +++++++++++ 7 files changed, 203 insertions(+), 1 deletion(-) diff --git a/include/rc_runtime_types.h b/include/rc_runtime_types.h index 9baf62b4..d8a7db65 100644 --- a/include/rc_runtime_types.h +++ b/include/rc_runtime_types.h @@ -354,7 +354,14 @@ enum { RC_FORMAT_FLOAT3, RC_FORMAT_FLOAT4, RC_FORMAT_FLOAT5, - RC_FORMAT_FLOAT6 + RC_FORMAT_FLOAT6, + RC_FORMAT_FIXED1, + RC_FORMAT_FIXED2, + RC_FORMAT_FIXED3, + RC_FORMAT_TENS, + RC_FORMAT_HUNDREDS, + RC_FORMAT_THOUSANDS, + RC_FORMAT_UNSIGNED_VALUE }; RC_EXPORT int RC_CCONV rc_parse_format(const char* format_str); diff --git a/src/rc_client.c b/src/rc_client.c index 80b84b1b..0028f413 100644 --- a/src/rc_client.c +++ b/src/rc_client.c @@ -1701,6 +1701,13 @@ uint8_t rc_client_map_leaderboard_format(int format) case RC_FORMAT_FLOAT4: case RC_FORMAT_FLOAT5: case RC_FORMAT_FLOAT6: + case RC_FORMAT_FIXED1: + case RC_FORMAT_FIXED2: + case RC_FORMAT_FIXED3: + case RC_FORMAT_TENS: + case RC_FORMAT_HUNDREDS: + case RC_FORMAT_THOUSANDS: + case RC_FORMAT_UNSIGNED_VALUE: default: return RC_CLIENT_LEADERBOARD_FORMAT_VALUE; } diff --git a/src/rcheevos/format.c b/src/rcheevos/format.c index c797faeb..0aa44ee6 100644 --- a/src/rcheevos/format.c +++ b/src/rcheevos/format.c @@ -14,6 +14,9 @@ int rc_parse_format(const char* format_str) { if (!strncmp(format_str, "LOAT", 4) && format_str[4] >= '1' && format_str[4] <= '6' && format_str[5] == '\0') { return RC_FORMAT_FLOAT1 + (format_str[4] - '1'); } + if (!strncmp(format_str, "IXED", 4) && format_str[4] >= '1' && format_str[4] <= '3' && format_str[5] == '\0') { + return RC_FORMAT_FIXED1 + (format_str[4] - '1'); + } break; @@ -24,6 +27,12 @@ int rc_parse_format(const char* format_str) { if (!strcmp(format_str, "IMESECS")) { return RC_FORMAT_SECONDS; } + if (!strcmp(format_str, "HOUSANDS")) { + return RC_FORMAT_THOUSANDS; + } + if (!strcmp(format_str, "ENS")) { + return RC_FORMAT_TENS; + } break; @@ -64,11 +73,25 @@ int rc_parse_format(const char* format_str) { break; + case 'U': + if (!strcmp(format_str, "NSIGNED")) { + return RC_FORMAT_UNSIGNED_VALUE; + } + + break; + case 'O': if (!strcmp(format_str, "THER")) { return RC_FORMAT_SCORE; } + break; + + case 'H': + if (!strcmp(format_str, "UNDREDS")) { + return RC_FORMAT_HUNDREDS; + } + break; } @@ -119,6 +142,22 @@ static int rc_format_value_centiseconds(char* buffer, size_t size, uint32_t cent return chars; } +static int rc_format_value_fixed(char* buffer, size_t size, const char* format, int32_t value, int32_t factor) +{ + if (value >= 0) + return snprintf(buffer, size, format, value / factor, value % factor); + + return snprintf(buffer, size, format, value / factor, (-value) % factor); +} + +static int rc_format_value_padded(char* buffer, size_t size, const char* format, int32_t value) +{ + if (value == 0) + return snprintf(buffer, size, "0"); + + return snprintf(buffer, size, format, value); +} + int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* value, int format) { int chars; rc_typed_value_t converted_value; @@ -192,6 +231,41 @@ int rc_format_typed_value(char* buffer, size_t size, const rc_typed_value_t* val rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_FLOAT); chars = snprintf(buffer, size, "%.6f", converted_value.value.f32); break; + + case RC_FORMAT_FIXED1: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_fixed(buffer, size, "%d.%u", converted_value.value.i32, 10); + break; + + case RC_FORMAT_FIXED2: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_fixed(buffer, size, "%d.%02u", converted_value.value.i32, 100); + break; + + case RC_FORMAT_FIXED3: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_fixed(buffer, size, "%d.%03u", converted_value.value.i32, 1000); + break; + + case RC_FORMAT_TENS: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_padded(buffer, size, "%d0", converted_value.value.i32); + break; + + case RC_FORMAT_HUNDREDS: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_padded(buffer, size, "%d00", converted_value.value.i32); + break; + + case RC_FORMAT_THOUSANDS: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_SIGNED); + chars = rc_format_value_padded(buffer, size, "%d000", converted_value.value.i32); + break; + + case RC_FORMAT_UNSIGNED_VALUE: + rc_typed_value_convert(&converted_value, RC_VALUE_TYPE_UNSIGNED); + chars = snprintf(buffer, size, "%u", converted_value.value.u32); + break; } return chars; diff --git a/src/rcheevos/richpresence.c b/src/rcheevos/richpresence.c index 31df3342..0dd660e6 100644 --- a/src/rcheevos/richpresence.c +++ b/src/rcheevos/richpresence.c @@ -189,6 +189,10 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c {"Float4", 6, RC_FORMAT_FLOAT4}, {"Float5", 6, RC_FORMAT_FLOAT5}, {"Float6", 6, RC_FORMAT_FLOAT6}, + {"Fixed1", 6, RC_FORMAT_FIXED1}, + {"Fixed2", 6, RC_FORMAT_FIXED2}, + {"Fixed3", 6, RC_FORMAT_FIXED3}, + {"Unsigned", 8, RC_FORMAT_UNSIGNED_VALUE} }; size_t i; diff --git a/test/rcheevos/test_format.c b/test/rcheevos/test_format.c index d8bbccf4..0d1e64fd 100644 --- a/test/rcheevos/test_format.c +++ b/test/rcheevos/test_format.c @@ -22,6 +22,7 @@ void test_format(void) { TEST_PARAMS3(test_format_value, RC_FORMAT_VALUE, 12345, "12345"); TEST_PARAMS3(test_format_value, RC_FORMAT_VALUE, -12345, "-12345"); TEST_PARAMS3(test_format_value, RC_FORMAT_VALUE, 0xFFFFFFFF, "-1"); + TEST_PARAMS3(test_format_value, RC_FORMAT_UNSIGNED_VALUE, 0xFFFFFFFF, "4294967295"); TEST_PARAMS3(test_format_value, RC_FORMAT_SCORE, 12345, "012345"); TEST_PARAMS3(test_format_value, RC_FORMAT_SECONDS, 45, "0:45"); TEST_PARAMS3(test_format_value, RC_FORMAT_SECONDS, 345, "5:45"); @@ -38,6 +39,30 @@ void test_format(void) { TEST_PARAMS3(test_format_value, RC_FORMAT_FRAMES, 345, "0:05.75"); TEST_PARAMS3(test_format_value, RC_FORMAT_FRAMES, 12345, "3:25.75"); TEST_PARAMS3(test_format_value, RC_FORMAT_FRAMES, 1234567, "5h42:56.11"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED1, 0, "0.0"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED1, 1, "0.1"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED1, 1234, "123.4"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED1, -1234, "-123.4"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED2, 0, "0.00"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED2, 1, "0.01"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED2, 1234, "12.34"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED2, -1234, "-12.34"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED3, 0, "0.000"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED3, 1, "0.001"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED3, 1234, "1.234"); + TEST_PARAMS3(test_format_value, RC_FORMAT_FIXED3, -1234, "-1.234"); + TEST_PARAMS3(test_format_value, RC_FORMAT_TENS, 0, "0"); + TEST_PARAMS3(test_format_value, RC_FORMAT_TENS, 1, "10"); + TEST_PARAMS3(test_format_value, RC_FORMAT_TENS, 1234, "12340"); + TEST_PARAMS3(test_format_value, RC_FORMAT_TENS, -1234, "-12340"); + TEST_PARAMS3(test_format_value, RC_FORMAT_HUNDREDS, 0, "0"); + TEST_PARAMS3(test_format_value, RC_FORMAT_HUNDREDS, 1, "100"); + TEST_PARAMS3(test_format_value, RC_FORMAT_HUNDREDS, 1234, "123400"); + TEST_PARAMS3(test_format_value, RC_FORMAT_HUNDREDS, -1234, "-123400"); + TEST_PARAMS3(test_format_value, RC_FORMAT_THOUSANDS, 0, "0"); + TEST_PARAMS3(test_format_value, RC_FORMAT_THOUSANDS, 1, "1000"); + TEST_PARAMS3(test_format_value, RC_FORMAT_THOUSANDS, 1234, "1234000"); + TEST_PARAMS3(test_format_value, RC_FORMAT_THOUSANDS, -1234, "-1234000"); /* rc_parse_format */ TEST_PARAMS2(test_parse_format, "VALUE", RC_FORMAT_VALUE); @@ -50,6 +75,10 @@ void test_format(void) { TEST_PARAMS2(test_parse_format, "SCORE", RC_FORMAT_SCORE); TEST_PARAMS2(test_parse_format, "POINTS", RC_FORMAT_SCORE); TEST_PARAMS2(test_parse_format, "MILLISECS", RC_FORMAT_CENTISECS); + TEST_PARAMS2(test_parse_format, "TENS", RC_FORMAT_TENS); + TEST_PARAMS2(test_parse_format, "HUNDREDS", RC_FORMAT_HUNDREDS); + TEST_PARAMS2(test_parse_format, "THOUSANDS", RC_FORMAT_THOUSANDS); + TEST_PARAMS2(test_parse_format, "UNSIGNED", RC_FORMAT_UNSIGNED_VALUE); TEST_PARAMS2(test_parse_format, "OTHER", RC_FORMAT_SCORE); TEST_PARAMS2(test_parse_format, "INVALID", RC_FORMAT_VALUE); @@ -64,5 +93,16 @@ void test_format(void) { TEST_PARAMS2(test_parse_format, "FLOAT7", RC_FORMAT_VALUE); TEST_PARAMS2(test_parse_format, "FLOAT10", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED0", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED1", RC_FORMAT_FIXED1); + TEST_PARAMS2(test_parse_format, "FIXED2", RC_FORMAT_FIXED2); + TEST_PARAMS2(test_parse_format, "FIXED3", RC_FORMAT_FIXED3); + TEST_PARAMS2(test_parse_format, "FIXED4", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED5", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED6", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED7", RC_FORMAT_VALUE); + TEST_PARAMS2(test_parse_format, "FIXED10", RC_FORMAT_VALUE); + TEST_SUITE_END(); } diff --git a/test/rcheevos/test_richpresence.c b/test/rcheevos/test_richpresence.c index da9d2ef8..45a694b1 100644 --- a/test/rcheevos/test_richpresence.c +++ b/test/rcheevos/test_richpresence.c @@ -486,6 +486,28 @@ static void test_macro_value_divide_by_self() { assert_richpresence_output(richpresence, &memory, "Result is 0"); } +static void test_macro_hundreds() { + uint8_t ram[] = { 0x00, 0x12, 0x34, 0xAB, 0x56 }; + memory_t memory; + rc_richpresence_t* richpresence; + char buffer[1024]; + + memory.ram = ram; + memory.size = sizeof(ram); + + assert_parse_richpresence(&richpresence, buffer, "Format:Value\nFormatType=HUNDREDS\n\nDisplay:\nResult is @Value(0xH00)"); + assert_richpresence_output(richpresence, &memory, "Result is 0"); + + ram[0] = 18; + assert_richpresence_output(richpresence, &memory, "Result is 1800"); + + ram[0] = 255; + assert_richpresence_output(richpresence, &memory, "Result is 25500"); + + ram[0] = 0; + assert_richpresence_output(richpresence, &memory, "Result is 0"); +} + static void test_macro_frames() { uint8_t ram[] = { 0x00, 0x12, 0x34, 0xAB, 0x56 }; memory_t memory; @@ -1047,6 +1069,19 @@ static void test_builtin_macro_float(const char* macro, const char* expected) { assert_richpresence_output(richpresence, &memory, expected); } +static void test_builtin_macro_unsigned_large() { + uint8_t ram[] = { 0x85, 0xE2, 0x59, 0xC7 }; + memory_t memory; + rc_richpresence_t* richpresence; + char buffer[256]; + + memory.ram = ram; + memory.size = sizeof(ram); + + assert_parse_richpresence(&richpresence, buffer, "Display:\n@Unsigned(0xX0)"); + assert_richpresence_output(richpresence, &memory, "3344556677"); +} + static void test_builtin_macro_override() { uint8_t ram[] = { 0x39, 0x30 }; memory_t memory; @@ -1249,6 +1284,9 @@ void test_richpresence(void) { TEST(test_macro_value_divide_by_zero); TEST(test_macro_value_divide_by_self); + /* hundreds macro */ + TEST(test_macro_hundreds); + /* frames macros */ TEST(test_macro_frames); @@ -1312,6 +1350,11 @@ void test_richpresence(void) { TEST_PARAMS2(test_builtin_macro_float, "Float4", "77.1339"); TEST_PARAMS2(test_builtin_macro_float, "Float5", "77.13393"); TEST_PARAMS2(test_builtin_macro_float, "Float6", "77.133926"); + TEST_PARAMS2(test_builtin_macro, "Fixed1", "1234.5"); + TEST_PARAMS2(test_builtin_macro, "Fixed2", "123.45"); + TEST_PARAMS2(test_builtin_macro, "Fixed3", "12.345"); + TEST_PARAMS2(test_builtin_macro, "Unsigned", "12345"); + TEST(test_builtin_macro_unsigned_large); TEST(test_builtin_macro_override); /* asciichar */ diff --git a/test/test_rc_client.c b/test/test_rc_client.c index f6e86e42..76ae119e 100644 --- a/test/test_rc_client.c +++ b/test/test_rc_client.c @@ -5054,6 +5054,31 @@ static void test_fetch_leaderboard_entries_client_error(void) rc_client_destroy(g_client); } +static void test_map_leaderboard_format(void) +{ + int i; + + for (i = 0; i < 30; ++i) { + switch (i) { + case RC_FORMAT_SECONDS: + case RC_FORMAT_CENTISECS: + case RC_FORMAT_MINUTES: + case RC_FORMAT_SECONDS_AS_MINUTES: + case RC_FORMAT_FRAMES: + ASSERT_NUM_EQUALS(rc_client_map_leaderboard_format(i), RC_CLIENT_LEADERBOARD_FORMAT_TIME); + break; + + case RC_FORMAT_SCORE: + ASSERT_NUM_EQUALS(rc_client_map_leaderboard_format(i), RC_CLIENT_LEADERBOARD_FORMAT_SCORE); + break; + + default: + ASSERT_NUM_EQUALS(rc_client_map_leaderboard_format(i), RC_CLIENT_LEADERBOARD_FORMAT_VALUE); + break; + } + } +} + /* ----- do frame ----- */ static void test_do_frame_bounds_check_system(void) @@ -8520,6 +8545,8 @@ void test_client(void) { TEST(test_fetch_leaderboard_entries_async_aborted); TEST(test_fetch_leaderboard_entries_client_error); + TEST(test_map_leaderboard_format); + /* do frame */ TEST(test_do_frame_bounds_check_system); TEST(test_do_frame_bounds_check_available);