mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-21 17:26:40 +02:00
Throwdowns: a grouped line score is no longer cut at its first separator
A Jam Session takes the score of the line just landed from the score HUD's text, with the script native ParseInt, which is the C library's atoi. atoi stops at the first character that is not a digit, so a line shown as 5,432 was read as 5 and added 5 to the player's total. Reported for Jam Sessions and Spot Battles: lines of 5,000 and more adding one or two points. ParseInt now reads groups of three digits whole, set apart by a comma, a full stop, an apostrophe or a space: "5,432" is 5432. Text that is not a grouped number is read exactly as before. The first few grouped scores are logged, to confirm the cause in play.
This commit is contained in:
1 parent
180cdc9c85
commit
30a320ff62
4 files changed
+103
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "native_throwdowns.h"
|
||||
#include "grouped_number.h"
|
||||
#include "throwdown_lab.h"
|
||||
#include "throwdown_relay.h"
|
||||
#include "virtual_player_names.h"
|
||||
@@ -566,10 +567,36 @@ void apply_throwdown_strings(Address base) noexcept {
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
// ParseInt, reading a grouped number whole (grouped_number.h). Any script may call it.
|
||||
int (*parse_int_original)(const char* const*){};
|
||||
int parse_int_hook(const char* const* text) {
|
||||
const auto parsed = throwdowns_text::parse_grouped_int(text ? *text : nullptr);
|
||||
if (parsed.grouped) {
|
||||
static std::atomic<unsigned> logged{};
|
||||
if (logged.fetch_add(1, std::memory_order_relaxed) < 12) {
|
||||
std::string shown(*text, strnlen(*text, 32));
|
||||
for (auto& ch : shown) if (static_cast<unsigned char>(ch) < 0x20) ch = ' ';
|
||||
logging::log(logging::Level::info, logging::Channel::progression,
|
||||
"Scripts: \"{}\" read as {} (the game's own ParseInt stops at the first separator).", shown, parsed.value);
|
||||
}
|
||||
}
|
||||
return parsed.value;
|
||||
}
|
||||
void install_parse_int(Address base) noexcept {
|
||||
try {
|
||||
if (read<std::array<unsigned char, 10>>(base + throwdowns::parse_int) != throwdowns::parse_int_prefix)
|
||||
throw std::runtime_error("ParseInt fingerprint differs; grouped scores are read as the game reads them");
|
||||
auto* const target = reinterpret_cast<void*>(base + throwdowns::parse_int);
|
||||
if (hook_prepare(target, reinterpret_cast<void*>(&parse_int_hook), reinterpret_cast<void**>(&parse_int_original)) != HookOk ||
|
||||
hook_enable(target) != HookOk)
|
||||
throw std::runtime_error("Cannot replace ParseInt; grouped scores are read as the game reads them");
|
||||
} catch (const std::exception& e) { logging::write(logging::Level::warning, logging::Channel::progression, e.what()); }
|
||||
}
|
||||
void initialize_native_throwdowns(Address base) noexcept {
|
||||
auto& s = state();
|
||||
if (s.attempted || !base) return;
|
||||
s.attempted = true; s.base = base;
|
||||
install_parse_int(base);
|
||||
struct Hook { Address rva; std::array<unsigned char, 19> prefix; void* replacement; void** original; };
|
||||
const std::array hooks{
|
||||
Hook{throwdowns::mode_available, throwdowns::mode_available_prefix, reinterpret_cast<void*>(&mode_available_hook), reinterpret_cast<void**>(&s.mode_available)},
|
||||
|
||||
Reference in new issue
Block a user