Patch all BB online BattleParam tables before Brutal Peeps rooms

This commit is contained in:
2026-06-06 23:33:11 -04:00
parent 98dd6b8913
commit 8dd966a5d7
+64 -46
View File
@@ -775,20 +775,22 @@ static std::string bb_stream_file_data_for_client(std::shared_ptr<Client> c) {
} }
static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_hp_patch_bb_now( static std::vector<std::pair<std::string, std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>>>> send_brutal_peeps_hp_patch_bb_now(
std::shared_ptr<Client> c, std::shared_ptr<Client> c,
int64_t tier) { int64_t tier) {
std::vector<std::pair<std::string, std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>>>> promises;
if (c->version() != Version::BB_V4) { if (c->version() != Version::BB_V4) {
return nullptr; return promises;
} }
if (!c->check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) || if (!c->check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
!c->check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) { !c->check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
c->log.warning_f("Skipping Brutal Peeps HP client patch because client does not support executable send_function_call"); c->log.warning_f("Skipping Brutal Peeps HP client patch because client does not support executable send_function_call");
return nullptr; return promises;
} }
if (!c->channel->connected()) { if (!c->channel->connected()) {
c->log.warning_f("Skipping Brutal Peeps HP client patch because client is disconnected"); c->log.warning_f("Skipping Brutal Peeps HP client patch because client is disconnected");
return nullptr; return promises;
} }
try { try {
@@ -796,48 +798,36 @@ static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_h
const auto* brutal_peeps_def = brutal_peeps_tier_definition(tier); const auto* brutal_peeps_def = brutal_peeps_tier_definition(tier);
if ((tier >= 0) && !brutal_peeps_def) { if ((tier >= 0) && !brutal_peeps_def) {
c->log.warning_f("Skipping Brutal Peeps HP client patch for invalid tier {}", tier); c->log.warning_f("Skipping Brutal Peeps HP client patch for invalid tier {}", tier);
return nullptr; return promises;
} }
const double mult = brutal_peeps_def ? brutal_peeps_def->enemy_hp_multiplier : 1.0; const double mult = brutal_peeps_def ? brutal_peeps_def->enemy_hp_multiplier : 1.0;
const std::string bp_filename = [&]() -> std::string { std::vector<std::string> bp_filenames;
auto l = c->lobby.lock(); auto l = c->lobby.lock();
if (l && l->is_game()) { if (l && l->is_game()) {
switch (l->episode) { switch (l->episode) {
case Episode::EP1: case Episode::EP1:
return "BattleParamEntry_on.dat"; bp_filenames.emplace_back("BattleParamEntry_on.dat");
break;
case Episode::EP2: case Episode::EP2:
return "BattleParamEntry_lab_on.dat"; bp_filenames.emplace_back("BattleParamEntry_lab_on.dat");
break;
case Episode::EP4: case Episode::EP4:
return "BattleParamEntry_ep4_on.dat"; bp_filenames.emplace_back("BattleParamEntry_ep4_on.dat");
break;
default: default:
break; break;
} }
} }
return "BattleParamEntry_on.dat";
}();
const BBStreamFile::Entry* bp_entry = nullptr; // Before the room exists, we don't know which episode the player will pick.
// Patch all online BB BattleParam tables so EP2/EP4 HP is already scaled before enemies initialize.
for (const auto& sf_entry : s->bb_stream_file->entries) { if (bp_filenames.empty()) {
if (sf_entry.filename == bp_filename) { bp_filenames.emplace_back("BattleParamEntry_on.dat");
bp_entry = &sf_entry; bp_filenames.emplace_back("BattleParamEntry_lab_on.dat");
break; bp_filenames.emplace_back("BattleParamEntry_ep4_on.dat");
} }
}
if (!bp_entry) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} not found in BB stream file", bp_filename);
return nullptr;
}
if ((bp_entry->offset > s->bb_stream_file->data.size()) ||
(bp_entry->size > (s->bb_stream_file->data.size() - bp_entry->offset)) ||
(bp_entry->size < sizeof(BattleParamsIndex::Table))) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: invalid {} range", bp_filename);
return nullptr;
}
const char* vanilla_data = s->bb_stream_file->data.data() + bp_entry->offset;
constexpr uint32_t scan_start = 0x16760000; constexpr uint32_t scan_start = 0x16760000;
constexpr uint32_t scan_end = 0x16A90000; constexpr uint32_t scan_end = 0x16A90000;
@@ -849,15 +839,6 @@ static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_h
constexpr uint32_t stats_row_size = 0x24; constexpr uint32_t stats_row_size = 0x24;
constexpr uint32_t num_bp_rows = 0x60; constexpr uint32_t num_bp_rows = 0x60;
if (bp_entry->size < signature_size) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} too small for signature", bp_filename);
return nullptr;
}
if (bp_entry->size < (ultimate_hp_base_offset + ((num_bp_rows - 1) * stats_row_size) + 2)) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} too small for Ultimate HP table", bp_filename);
return nullptr;
}
auto append_u32l = +[](std::string& out, uint32_t v) { auto append_u32l = +[](std::string& out, uint32_t v) {
out.push_back(static_cast<char>(v & 0xFF)); out.push_back(static_cast<char>(v & 0xFF));
out.push_back(static_cast<char>((v >> 8) & 0xFF)); out.push_back(static_cast<char>((v >> 8) & 0xFF));
@@ -879,6 +860,39 @@ static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_h
return static_cast<uint16_t>(scaled); return static_cast<uint16_t>(scaled);
}; };
auto fn = s->client_functions->get("PsoPeepsBrutalPeepsHP", c->specific_version);
for (const auto& bp_filename : bp_filenames) {
const BBStreamFile::Entry* bp_entry = nullptr;
for (const auto& sf_entry : s->bb_stream_file->entries) {
if (sf_entry.filename == bp_filename) {
bp_entry = &sf_entry;
break;
}
}
if (!bp_entry) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} not found in BB stream file", bp_filename);
continue;
}
if ((bp_entry->offset > s->bb_stream_file->data.size()) ||
(bp_entry->size > (s->bb_stream_file->data.size() - bp_entry->offset))) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: invalid {} range", bp_filename);
continue;
}
const char* vanilla_data = s->bb_stream_file->data.data() + bp_entry->offset;
if (bp_entry->size < signature_size) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} too small for signature", bp_filename);
continue;
}
if (bp_entry->size < (ultimate_hp_base_offset + ((num_bp_rows - 1) * stats_row_size) + 2)) {
c->log.warning_f("Skipping Brutal Peeps HP client patch: {} too small for Ultimate HP table", bp_filename);
continue;
}
std::string suffix; std::string suffix;
append_u32l(suffix, scan_start); append_u32l(suffix, scan_start);
append_u32l(suffix, scan_end); append_u32l(suffix, scan_end);
@@ -907,8 +921,6 @@ static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_h
suffix[14] = static_cast<char>((patch_entry_count >> 16) & 0xFF); suffix[14] = static_cast<char>((patch_entry_count >> 16) & 0xFF);
suffix[15] = static_cast<char>((patch_entry_count >> 24) & 0xFF); suffix[15] = static_cast<char>((patch_entry_count >> 24) & 0xFF);
auto fn = s->client_functions->get("PsoPeepsBrutalPeepsHP", c->specific_version);
auto promise = std::make_shared<AsyncPromise<C_ExecuteCodeResult_B3>>(); auto promise = std::make_shared<AsyncPromise<C_ExecuteCodeResult_B3>>();
c->function_call_response_queue.emplace_back(promise); c->function_call_response_queue.emplace_back(promise);
@@ -921,15 +933,17 @@ static std::shared_ptr<AsyncPromise<C_ExecuteCodeResult_B3>> send_brutal_peeps_h
suffix.size()); suffix.size());
c->enabled_flags |= fn->client_flag; c->enabled_flags |= fn->client_flag;
promises.emplace_back(bp_filename, promise);
c->log.info_f("Brutal Peeps HP client patch sent for {}: tier={} mult={:g} patch_entries={} scan={:08X}-{:08X}", c->log.info_f("Brutal Peeps HP client patch sent for {}: tier={} mult={:g} patch_entries={} scan={:08X}-{:08X}",
bp_filename, tier, mult, patch_entry_count, scan_start, scan_end); bp_filename, tier, mult, patch_entry_count, scan_start, scan_end);
}
return promise; return promises;
} catch (const std::exception& e) { } catch (const std::exception& e) {
c->log.warning_f("Failed to send Brutal Peeps HP client patch: {}", e.what()); c->log.warning_f("Failed to send Brutal Peeps HP client patch: {}", e.what());
return nullptr; return promises;
} }
} }
@@ -937,14 +951,19 @@ asio::awaitable<void> send_brutal_peeps_hp_patch_bb(std::shared_ptr<Client> c, i
try { try {
co_await prepare_client_for_patches(c); co_await prepare_client_for_patches(c);
auto promise = send_brutal_peeps_hp_patch_bb_now(c, tier); auto promises = send_brutal_peeps_hp_patch_bb_now(c, tier);
for (auto& it : promises) {
const auto& filename = it.first;
auto& promise = it.second;
if (promise && c->channel->connected()) { if (promise && c->channel->connected()) {
auto result = co_await promise->get(); auto result = co_await promise->get();
c->log.info_f("Brutal Peeps HP client patch result: tier={} return_value={:08X} checksum={:08X}", c->log.info_f("Brutal Peeps HP client patch result for {}: tier={} return_value={:08X} checksum={:08X}",
filename,
tier, tier,
static_cast<uint32_t>(result.return_value), static_cast<uint32_t>(result.return_value),
static_cast<uint32_t>(result.checksum)); static_cast<uint32_t>(result.checksum));
} }
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
c->log.warning_f("Failed to complete Brutal Peeps HP client patch: {}", e.what()); c->log.warning_f("Failed to complete Brutal Peeps HP client patch: {}", e.what());
@@ -952,7 +971,6 @@ asio::awaitable<void> send_brutal_peeps_hp_patch_bb(std::shared_ptr<Client> c, i
} }
void send_stream_file_index_bb(std::shared_ptr<Client> c) { void send_stream_file_index_bb(std::shared_ptr<Client> c) {
auto s = c->require_server_state(); auto s = c->require_server_state();