diff --git a/s4_league_fps_unlock.cpp b/s4_league_fps_unlock.cpp index f836ed1..1263fe3 100644 --- a/s4_league_fps_unlock.cpp +++ b/s4_league_fps_unlock.cpp @@ -118,6 +118,7 @@ static uint32_t target_frametime_ns; static uint32_t target_frametime_100ns; static float frametime; +static uint64_t frametime_accumulated = 0; static uint8_t weapon_slot; static float set_drop_val; @@ -228,6 +229,139 @@ struct ctx_01642f30{ }; static struct ctx_01642f30 *(*fetch_ctx_01642f30)(void) = (struct ctx_01642f30 *(*)(void)) 0x004ae0a0; +// hook random spread calculation +struct ctx_calculate_random_spread{ + uint8_t unknown[0x12c]; + uint32_t shooting; + uint8_t unknown_2[0x18]; + uint32_t spread_type; + uint8_t unknown_3[0x3c]; + uint32_t inner_verdict; + uint32_t inner_verdict_flipped; + uint32_t inner_verdict_xor_key; + uint8_t unknown_4[0x30]; + uint32_t outer_verdict; + uint32_t outer_verdict_flipped; + uint32_t outer_verdict_xor_key; +}; + +static void (__attribute__((thiscall)) *orig_calculate_weapon_spread)(struct ctx_calculate_random_spread *, float, uint32_t); +void __attribute__((thiscall)) patched_calculate_weapon_spread(struct ctx_calculate_random_spread *ctx, float param_1, uint32_t param_2){ + orig_calculate_weapon_spread(ctx, param_1, param_2); + uint32_t inner_verdict = ctx->inner_verdict ^ ctx->inner_verdict_xor_key; + float inner_verdict_f = *(float *)&inner_verdict; + uint32_t outer_verdict = ctx->outer_verdict ^ ctx->outer_verdict_xor_key; + float outer_verdict_f = *(float *)&outer_verdict; + LOG_VERBOSE("%s: ctx 0x%08x", __FUNCTION__, ctx); + LOG_VERBOSE("%s: param_1: %f, param_2: %u", __FUNCTION__, param_1, param_2); + LOG_VERBOSE("%s: inner_verdict: %f, outer_verdict: %f", __FUNCTION__, inner_verdict_f, outer_verdict_f); + LOG_VERBOSE("%s: ret chain 0x%08x -> 0x%08x -> 0x%08x -> 0x%08x", __FUNCTION__, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2), __builtin_return_address(3)); + + if(__builtin_return_address(1) == (void *)0x005ab63a && ctx->spread_type == 2){ + LOG_VERBOSE("%s: deployed turret, ctx 0x%08x", __FUNCTION__, ctx); + static bool was_shooting = false; + + // re-implement 60fps like behavior + if(ctx->shooting != 0){ + LOG_VERBOSE("%s: shooting", __FUNCTION__, ctx); + static uint32_t shooting_time_ms = 0; + static uint64_t last_shot_ts = 0; + const uint32_t peak_spread_ms = 67; + const float peak_spread_ms_f = peak_spread_ms * 1.0; + + uint32_t time_delta = frametime_accumulated - last_shot_ts; + + if(was_shooting){ + if(last_shot_ts != 0){ + shooting_time_ms += time_delta; + }else{ + shooting_time_ms = 0; + } + if(shooting_time_ms > peak_spread_ms_f){ + shooting_time_ms = peak_spread_ms_f; + } + }else{ + if(last_shot_ts != 0 && shooting_time_ms > time_delta){ + shooting_time_ms -= time_delta; + }else{ + shooting_time_ms = 0; + } + } + + LOG_VERBOSE("%s: was_shooting: %s", __FUNCTION__, was_shooting ? "true" : "false"); + LOG_VERBOSE("%s: time_delta: %lu", __FUNCTION__, time_delta); + LOG_VERBOSE("%s: shooting_time_ms: %u", __FUNCTION__, shooting_time_ms); + LOG_VERBOSE("%s: frametime_accumulated: %lu", __FUNCTION__, frametime_accumulated); + LOG_VERBOSE("%s: frametime: %f", __FUNCTION__, frametime); + + last_shot_ts = frametime_accumulated; + was_shooting = true; + + const float inner_spread_var = 0.15; + const float outer_spread_var = 0.5; + + float inner_rand = rand() / (RAND_MAX * 1.0); + float outer_rand = rand() / (RAND_MAX * 1.0); + + float shooting_time_ms_f = shooting_time_ms * 1.0; + + LOG_VERBOSE("%s: shooting_time_ms_f: %f", __FUNCTION__, shooting_time_ms_f); + LOG_VERBOSE("%s: original inner_verdict: %f, original outer_verdict: %f", __FUNCTION__, inner_verdict_f, outer_verdict_f); + + inner_verdict_f = 1.0 * (shooting_time_ms_f / peak_spread_ms_f) - inner_spread_var * inner_rand; + if(inner_verdict_f < 0){ + inner_verdict_f = 0.0; + } + outer_verdict_f = outer_spread_var * outer_rand; + + inner_verdict = *(uint32_t *)&inner_verdict_f; + ctx->inner_verdict = inner_verdict ^ ctx->inner_verdict_xor_key; + ctx->inner_verdict_flipped = ~ctx->inner_verdict; + + outer_verdict = *(uint32_t *)&outer_verdict_f; + ctx->outer_verdict = outer_verdict ^ ctx->outer_verdict_xor_key; + ctx->outer_verdict_flipped = ~ctx->outer_verdict; + LOG_VERBOSE("%s: inner_verdict: %f, outer_verdict: %f", __FUNCTION__, inner_verdict_f, outer_verdict_f); + }else{ + LOG_VERBOSE("%s: not shooting", __FUNCTION__, ctx); + was_shooting = false; + } + } + + return; +} + +static void hook_calculate_weapon_spread(){ + LOG("hooking calculate_weapon_spread"); + + uint8_t intended_trampoline[] = { + // space for original instruction + 0, 0, 0, 0, 0, 0, 0, 0, 0, + // MOV eax,0058c809 + 0xb8, 0x09, 0xc8, 0x58, 0x00, + // JMP eax + 0xff, 0xe0 + }; + memcpy((void *)intended_trampoline, (void *)0x0058c800, 9); + + uint8_t intended_patch[] = { + // MOV eax, patched_calculate_weapon_spread + 0xb8, 0, 0, 0, 0, + // JMP eax + 0xff, 0xe0, + // nop nop + 0x90, 0x90 + }; + *(uint32_t *)&intended_patch[1] = (uint32_t)patched_calculate_weapon_spread; + + orig_calculate_weapon_spread = (void (__attribute__((thiscall)) *)(struct ctx_calculate_random_spread *, float, uint32_t))VirtualAlloc(NULL, sizeof(intended_trampoline), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + memcpy((void *)orig_calculate_weapon_spread, intended_trampoline, sizeof(intended_trampoline)); + DWORD old_protect; + VirtualProtect((void *)orig_calculate_weapon_spread, sizeof(intended_trampoline), PAGE_EXECUTE_READ, &old_protect); + + memcpy((void *)0x0058c800, intended_patch, sizeof(intended_patch)); +} + // can change active fov by hooking this struct ctx_fun_00766000{ uint8_t unknown[0x158]; @@ -686,6 +820,8 @@ void __attribute__((thiscall)) patched_game_tick(void *tick_ctx){ update_time_delta(&tctx); frametime = tctx.delta_t; + uint32_t frametime_uint = frametime; + frametime_accumulated = frametime_accumulated + frametime_uint; float new_speed_dampener = frametime * orig_speed_dampener / orig_fixed_frametime; // some kind of per frame speed filter, filters out smaller movement @@ -789,6 +925,7 @@ int init(){ //hook_switch_weapon_slot(); hook_fun_005e4020(); hook_fun_00766000(); + hook_calculate_weapon_spread(); experinmental_static_patches();