Add Brutal Peeps HP client patch
This commit is contained in:
@@ -2762,6 +2762,7 @@ static asio::awaitable<void> on_10_main_menu(std::shared_ptr<Client> c, uint32_t
|
|||||||
c->log.info_f("Brutal Peeps +{} selected from BB menu at level {}", tier, character_level);
|
c->log.info_f("Brutal Peeps +{} selected from BB menu at level {}", tier, character_level);
|
||||||
|
|
||||||
co_await send_auto_patches_if_needed(c);
|
co_await send_auto_patches_if_needed(c);
|
||||||
|
co_await send_brutal_peeps_hp_patch_bb(c, tier);
|
||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
@@ -2774,6 +2775,7 @@ static asio::awaitable<void> on_10_main_menu(std::shared_ptr<Client> c, uint32_t
|
|||||||
case MainMenuItemID::GO_TO_LOBBY: {
|
case MainMenuItemID::GO_TO_LOBBY: {
|
||||||
c->selected_brutal_peeps_tier = -1;
|
c->selected_brutal_peeps_tier = -1;
|
||||||
co_await send_auto_patches_if_needed(c);
|
co_await send_auto_patches_if_needed(c);
|
||||||
|
co_await send_brutal_peeps_hp_patch_bb(c, -1);
|
||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (is_pre_v1(c->version())) {
|
if (is_pre_v1(c->version())) {
|
||||||
|
|||||||
@@ -775,6 +775,124 @@ static std::string bb_stream_file_data_for_client(std::shared_ptr<Client> c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asio::awaitable<void> send_brutal_peeps_hp_patch_bb(std::shared_ptr<Client> c, int64_t tier) {
|
||||||
|
if (c->version() != Version::BB_V4) {
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
if (!c->check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
|
||||||
|
!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");
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto s = c->require_server_state();
|
||||||
|
const auto* brutal_peeps_def = brutal_peeps_tier_definition(tier);
|
||||||
|
if ((tier >= 0) && !brutal_peeps_def) {
|
||||||
|
c->log.warning_f("Skipping Brutal Peeps HP client patch for invalid tier {}", tier);
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double mult = brutal_peeps_def ? brutal_peeps_def->enemy_hp_multiplier : 1.0;
|
||||||
|
const BBStreamFile::Entry* bp_entry = nullptr;
|
||||||
|
|
||||||
|
for (const auto& sf_entry : s->bb_stream_file->entries) {
|
||||||
|
if (sf_entry.filename == "BattleParamEntry_on.dat") {
|
||||||
|
bp_entry = &sf_entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bp_entry) {
|
||||||
|
c->log.warning_f("Skipping Brutal Peeps HP client patch: BattleParamEntry_on.dat not found in BB stream file");
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
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 BattleParamEntry_on.dat range");
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* vanilla_data = s->bb_stream_file->data.data() + bp_entry->offset;
|
||||||
|
std::string target_data(vanilla_data, bp_entry->size);
|
||||||
|
|
||||||
|
auto* table = reinterpret_cast<BattleParamsIndex::Table*>(target_data.data());
|
||||||
|
size_t ultimate_index = static_cast<size_t>(Difficulty::ULTIMATE);
|
||||||
|
|
||||||
|
auto scale_u16 = [mult](uint32_t v) -> uint16_t {
|
||||||
|
if (v == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t scaled = static_cast<uint32_t>((static_cast<double>(v) * mult) + 0.5);
|
||||||
|
if (scaled < 1) {
|
||||||
|
scaled = 1;
|
||||||
|
}
|
||||||
|
if (scaled > 0xFFFF) {
|
||||||
|
scaled = 0xFFFF;
|
||||||
|
}
|
||||||
|
return static_cast<uint16_t>(scaled);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t z = 0; z < 0x60; z++) {
|
||||||
|
auto& stats = table->stats[ultimate_index][z];
|
||||||
|
stats.char_stats.hp = scale_u16(stats.char_stats.hp);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t scan_start = 0x16760000;
|
||||||
|
constexpr uint32_t scan_end = 0x16A90000;
|
||||||
|
constexpr uint32_t signature_size = 64;
|
||||||
|
constexpr uint32_t hp_patch_bytes = 0x60 * 2;
|
||||||
|
|
||||||
|
if (bp_entry->size < signature_size) {
|
||||||
|
c->log.warning_f("Skipping Brutal Peeps HP client patch: BattleParamEntry_on.dat too small for signature");
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string suffix;
|
||||||
|
suffix.append(vanilla_data, signature_size);
|
||||||
|
|
||||||
|
auto append_u32l = +[](std::string& out, uint32_t v) {
|
||||||
|
out.push_back(static_cast<char>(v & 0xFF));
|
||||||
|
out.push_back(static_cast<char>((v >> 8) & 0xFF));
|
||||||
|
out.push_back(static_cast<char>((v >> 16) & 0xFF));
|
||||||
|
out.push_back(static_cast<char>((v >> 24) & 0xFF));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t z = 0; z < 0x60; z++) {
|
||||||
|
const auto& hp = table->stats[ultimate_index][z].char_stats.hp;
|
||||||
|
uint32_t hp_offset = reinterpret_cast<const char*>(&hp) - target_data.data();
|
||||||
|
const uint8_t* hp_bytes = reinterpret_cast<const uint8_t*>(&hp);
|
||||||
|
|
||||||
|
append_u32l(suffix, hp_offset);
|
||||||
|
suffix.push_back(static_cast<char>(hp_bytes[0]));
|
||||||
|
|
||||||
|
append_u32l(suffix, hp_offset + 1);
|
||||||
|
suffix.push_back(static_cast<char>(hp_bytes[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
co_await prepare_client_for_patches(c);
|
||||||
|
auto fn = s->client_functions->get("PsoPeepsBrutalPeepsHP", c->specific_version);
|
||||||
|
co_await send_function_call(
|
||||||
|
c,
|
||||||
|
fn,
|
||||||
|
{
|
||||||
|
{"scan_start", scan_start},
|
||||||
|
{"scan_end", scan_end},
|
||||||
|
{"signature_size", signature_size},
|
||||||
|
{"patch_count", hp_patch_bytes},
|
||||||
|
},
|
||||||
|
suffix.data(),
|
||||||
|
suffix.size());
|
||||||
|
|
||||||
|
c->log.info_f("Brutal Peeps HP client patch sent: tier={} mult={:g} patch_bytes={} scan={:08X}-{:08X}",
|
||||||
|
tier, mult, hp_patch_bytes, scan_start, scan_end);
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
c->log.warning_f("Failed to send Brutal Peeps HP client patch: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ void send_guild_card_header_bb(std::shared_ptr<Client> c);
|
|||||||
void send_guild_card_chunk_bb(std::shared_ptr<Client> c, size_t chunk_index);
|
void send_guild_card_chunk_bb(std::shared_ptr<Client> c, size_t chunk_index);
|
||||||
void send_stream_file_index_bb(std::shared_ptr<Client> c);
|
void send_stream_file_index_bb(std::shared_ptr<Client> c);
|
||||||
void send_stream_file_chunk_bb(std::shared_ptr<Client> c, uint32_t chunk_index);
|
void send_stream_file_chunk_bb(std::shared_ptr<Client> c, uint32_t chunk_index);
|
||||||
|
asio::awaitable<void> send_brutal_peeps_hp_patch_bb(std::shared_ptr<Client> c, int64_t tier);
|
||||||
void send_approve_player_choice_bb(std::shared_ptr<Client> c);
|
void send_approve_player_choice_bb(std::shared_ptr<Client> c);
|
||||||
void send_complete_player_bb(std::shared_ptr<Client> c);
|
void send_complete_player_bb(std::shared_ptr<Client> c);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
.meta key="PsoPeepsBrutalPeepsHP"
|
||||||
|
.meta name="Brutal Peeps HP"
|
||||||
|
.meta description="Applies Brutal Peeps\nenemy HP scaling"
|
||||||
|
.meta show_return_value
|
||||||
|
|
||||||
|
.versions 50YJ 59NJ 59NL
|
||||||
|
|
||||||
|
entry_ptr:
|
||||||
|
reloc0:
|
||||||
|
.offsetof start
|
||||||
|
|
||||||
|
start:
|
||||||
|
push ebx
|
||||||
|
push esi
|
||||||
|
push edi
|
||||||
|
push ebp
|
||||||
|
|
||||||
|
jmp get_data_ptr
|
||||||
|
|
||||||
|
get_data_ptr_ret:
|
||||||
|
pop ebx
|
||||||
|
|
||||||
|
mov esi, [ebx + scan_start - data] # candidate ptr
|
||||||
|
mov edx, [ebx + scan_end - data] # scan end
|
||||||
|
mov ecx, [ebx + signature_size - data] # signature size
|
||||||
|
sub edx, ecx # scan limit = end - sig_size
|
||||||
|
lea edi, [ebx + payload - data] # signature ptr
|
||||||
|
|
||||||
|
scan_again:
|
||||||
|
cmp esi, edx
|
||||||
|
ja not_found
|
||||||
|
|
||||||
|
xor ebp, ebp
|
||||||
|
|
||||||
|
compare_again:
|
||||||
|
cmp ebp, ecx
|
||||||
|
jae found_table
|
||||||
|
|
||||||
|
mov al, [esi + ebp]
|
||||||
|
cmp al, [edi + ebp]
|
||||||
|
jne next_candidate
|
||||||
|
|
||||||
|
inc ebp
|
||||||
|
jmp compare_again
|
||||||
|
|
||||||
|
next_candidate:
|
||||||
|
inc esi
|
||||||
|
jmp scan_again
|
||||||
|
|
||||||
|
found_table:
|
||||||
|
# esi = BattleParamEntry_on.dat base
|
||||||
|
mov ecx, [ebx + patch_count - data]
|
||||||
|
mov edi, [ebx + signature_size - data]
|
||||||
|
lea edi, [ebx + payload - data + edi] # patch entry ptr after signature
|
||||||
|
|
||||||
|
patch_again:
|
||||||
|
test ecx, ecx
|
||||||
|
jz done
|
||||||
|
|
||||||
|
mov edx, [edi] # offset from table base
|
||||||
|
mov al, [edi + 4] # byte value
|
||||||
|
mov [esi + edx], al
|
||||||
|
|
||||||
|
add edi, 5
|
||||||
|
dec ecx
|
||||||
|
jmp patch_again
|
||||||
|
|
||||||
|
done:
|
||||||
|
mov eax, esi # return found table base
|
||||||
|
jmp return
|
||||||
|
|
||||||
|
not_found:
|
||||||
|
xor eax, eax
|
||||||
|
|
||||||
|
return:
|
||||||
|
pop ebp
|
||||||
|
pop edi
|
||||||
|
pop esi
|
||||||
|
pop ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
get_data_ptr:
|
||||||
|
call get_data_ptr_ret
|
||||||
|
|
||||||
|
data:
|
||||||
|
scan_start:
|
||||||
|
.data 0
|
||||||
|
scan_end:
|
||||||
|
.data 0
|
||||||
|
signature_size:
|
||||||
|
.data 0
|
||||||
|
patch_count:
|
||||||
|
.data 0
|
||||||
|
payload:
|
||||||
|
# Server suffix:
|
||||||
|
# signature bytes
|
||||||
|
# repeated patch entries:
|
||||||
|
# uint32_t offset
|
||||||
|
# uint8_t value
|
||||||
Reference in New Issue
Block a user