mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 20:17:24 +08:00
Protection improvements (#3146)
This commit is contained in:
parent
84d76f2038
commit
ee92caa889
@ -2,6 +2,7 @@
|
||||
#include "core/scr_globals.hpp"
|
||||
#include "gta_util.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "util/math.hpp"
|
||||
|
||||
#include <network/CCommunications.hpp>
|
||||
#include <network/Network.hpp>
|
||||
@ -18,7 +19,7 @@ namespace big
|
||||
uint64_t host_token;
|
||||
g_pointers->m_gta.m_generate_uuid(&host_token);
|
||||
|
||||
host_token = g.session.force_session_host ? (rand() % 10000) : host_token;
|
||||
host_token = g.session.force_session_host ? math::rand(10000) : host_token;
|
||||
|
||||
*g_pointers->m_gta.m_host_token = host_token;
|
||||
|
||||
|
@ -211,5 +211,5 @@ namespace big::functions
|
||||
|
||||
using can_create_vehicle = bool (*)();
|
||||
|
||||
using get_unk_weapon = void* (*) (CPed*);
|
||||
using get_searchlight = void* (*) (CPed*);
|
||||
}
|
||||
|
@ -485,7 +485,6 @@ enum class eNetworkEvents : uint16_t
|
||||
REQUEST_DETACHMENT_EVENT,
|
||||
KICK_VOTES_EVENT,
|
||||
GIVE_PICKUP_REWARDS_EVENT,
|
||||
NETWORK_CRC_HASH_CHECK_EVENT,
|
||||
BLOW_UP_VEHICLE_EVENT,
|
||||
NETWORK_SPECIAL_FIRE_EQUIPPED_WEAPON,
|
||||
NETWORK_RESPONDED_TO_THREAT_EVENT,
|
||||
|
@ -187,7 +187,6 @@ namespace big
|
||||
functions::fipackfile_unmount m_fipackfile_unmount;
|
||||
functions::fipackfile_close_archive m_fipackfile_close_archive;
|
||||
|
||||
PVOID m_invalid_mods_crash_detour;
|
||||
PVOID m_invalid_decal_crash;
|
||||
PVOID m_task_parachute_object;
|
||||
PVOID m_task_ambient_clips;
|
||||
@ -305,6 +304,7 @@ namespace big
|
||||
functions::get_host_array_handler_by_index m_get_host_array_handler_by_index;
|
||||
|
||||
PVOID m_error_message_box;
|
||||
PVOID m_error_message_box_2;
|
||||
|
||||
functions::get_title_caption_error_message_box m_get_title_caption_error_message_box;
|
||||
|
||||
@ -373,9 +373,9 @@ namespace big
|
||||
PVOID m_format_int;
|
||||
|
||||
PVOID m_searchlight_crash;
|
||||
functions::get_unk_weapon m_get_unk_weapon;
|
||||
functions::get_searchlight m_get_searchlight;
|
||||
|
||||
GenericPool** m_clone_create_pool; // this is not a normal pool
|
||||
GenericPool** m_vehicle_allocator; // this is not a normal pool
|
||||
|
||||
PVOID m_write_physical_script_game_state_data_node;
|
||||
};
|
||||
|
@ -65,7 +65,6 @@ namespace big
|
||||
detour_hook_helper::add<hooks::get_network_event_data>("GNED", g_pointers->m_gta.m_get_network_event_data);
|
||||
detour_hook_helper::add<hooks::write_player_gamer_data_node>("WPGDN", g_pointers->m_gta.m_write_player_gamer_data_node);
|
||||
|
||||
detour_hook_helper::add<hooks::invalid_mods_crash_detour>("IMCD", g_pointers->m_gta.m_invalid_mods_crash_detour);
|
||||
detour_hook_helper::add<hooks::invalid_decal>("IDC", g_pointers->m_gta.m_invalid_decal_crash);
|
||||
detour_hook_helper::add<hooks::task_parachute_object>("TPO", g_pointers->m_gta.m_task_parachute_object);
|
||||
detour_hook_helper::add<hooks::task_ambient_clips>("TAC", g_pointers->m_gta.m_task_ambient_clips);
|
||||
@ -125,6 +124,7 @@ namespace big
|
||||
detour_hook_helper::add<hooks::netfilter_handle_message>("NHM", g_pointers->m_gta.m_netfilter_handle_message);
|
||||
|
||||
detour_hook_helper::add<hooks::log_error_message_box>("E0MBH", g_pointers->m_gta.m_error_message_box);
|
||||
detour_hook_helper::add<hooks::log_error_message_box_2>("E0MBH2", g_pointers->m_gta.m_error_message_box_2);
|
||||
|
||||
detour_hook_helper::add<hooks::send_non_physical_player_data>("SNPPD", g_pointers->m_gta.m_send_non_physical_player_data);
|
||||
|
||||
|
@ -104,7 +104,6 @@ namespace big
|
||||
static void write_player_gamer_data_node(rage::netObject* player, CPlayerGamerDataNode* node);
|
||||
static void write_player_game_state_data_node(rage::netObject* player, CPlayerGameStateDataNode* node);
|
||||
|
||||
static void invalid_mods_crash_detour(int64_t a1, int64_t a2, int a3, char a4);
|
||||
static void invalid_decal(uintptr_t a1, int a2);
|
||||
static int task_parachute_object(uint64_t _this, int a2, int a3);
|
||||
static int task_ambient_clips(uint64_t _this, int a2, int a3);
|
||||
@ -164,6 +163,7 @@ namespace big
|
||||
static int netfilter_handle_message(__int64 filter, char* message, int flags);
|
||||
|
||||
static void log_error_message_box(rage::joaat_t joaated_error_code, bool a2);
|
||||
static void log_error_message_box_2(rage::joaat_t joaated_error_code);
|
||||
|
||||
static bool send_non_physical_player_data(CNetGamePlayer* player, __int64 message, int flags, void* a4, CNetGamePlayer* a5);
|
||||
|
||||
|
@ -38,4 +38,9 @@ namespace big
|
||||
|
||||
log_stack_trace();
|
||||
}
|
||||
|
||||
void hooks::log_error_message_box_2(rage::joaat_t joaated_error_code)
|
||||
{
|
||||
log_error_message_box(joaated_error_code, false);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void hooks::invalid_mods_crash_detour(int64_t a1, int64_t a2, int a3, char a4)
|
||||
{
|
||||
if (!*(int64_t*)(a1 + 0xD8)) [[unlikely]]
|
||||
return;
|
||||
g_hooking->get_original<hooks::invalid_mods_crash_detour>()(a1, a2, a3, a4);
|
||||
}
|
||||
}
|
@ -13,11 +13,22 @@ namespace big
|
||||
return;
|
||||
}
|
||||
|
||||
if (*g_pointers->m_gta.m_clone_create_pool && (*g_pointers->m_gta.m_clone_create_pool)->m_size < 2)
|
||||
switch (object_type)
|
||||
{
|
||||
// We don't have enough memory to handle this
|
||||
g_notification_service.push_warning("Protections", "Low net object pool size");
|
||||
return;
|
||||
case eNetObjType::NET_OBJ_TYPE_AUTOMOBILE:
|
||||
case eNetObjType::NET_OBJ_TYPE_BIKE:
|
||||
case eNetObjType::NET_OBJ_TYPE_BOAT:
|
||||
case eNetObjType::NET_OBJ_TYPE_HELI:
|
||||
case eNetObjType::NET_OBJ_TYPE_PLANE:
|
||||
case eNetObjType::NET_OBJ_TYPE_SUBMARINE:
|
||||
case eNetObjType::NET_OBJ_TYPE_TRAILER:
|
||||
case eNetObjType::NET_OBJ_TYPE_TRAIN:
|
||||
if ((*g_pointers->m_gta.m_vehicle_allocator)->m_size < 10) [[unlikely]]
|
||||
{
|
||||
// We don't have enough memory to handle this
|
||||
g_notification_service.push_warning("Protections", "Low vehicle allocator size");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto plyr = g_player_service->get_by_id(src->m_player_id);
|
||||
|
@ -38,6 +38,21 @@ namespace big
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_local_vehicle(int16_t net_id)
|
||||
{
|
||||
return g_local_player && g_local_player->m_vehicle && g_local_player->m_vehicle->m_net_object
|
||||
&& g_local_player->m_vehicle->m_driver == g_local_player && g_local_player->m_vehicle->m_net_object->m_object_id == net_id;
|
||||
}
|
||||
|
||||
inline bool is_in_vehicle(CPed* ped)
|
||||
{
|
||||
for (int i = 0; i < 15; i++)
|
||||
if (g_local_player->m_vehicle->m_passengers[i] == ped)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if bad event
|
||||
bool scan_weapon_damage_event(rage::netEventMgr* event_manager, CNetGamePlayer* player, CNetGamePlayer* target_player, int event_index, int event_handled_bitset, rage::datBitBuffer* buffer)
|
||||
@ -376,6 +391,96 @@ namespace big
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
bool scan_play_sound_event(player_ptr plyr, rage::datBitBuffer& buffer)
|
||||
{
|
||||
bool is_entity = buffer.Read<bool>(1);
|
||||
std::int16_t entity_net_id;
|
||||
rage::fvector3 position;
|
||||
|
||||
if (is_entity)
|
||||
{
|
||||
entity_net_id = buffer.Read<uint16_t>(13);
|
||||
}
|
||||
else
|
||||
{
|
||||
position.x = buffer.ReadSignedFloat(19, 1337.0f);
|
||||
position.y = buffer.ReadSignedFloat(19, 1337.0f);
|
||||
position.z = buffer.ReadFloat(19, 1337.0f);
|
||||
}
|
||||
|
||||
bool has_ref = buffer.Read<bool>(1);
|
||||
uint32_t ref_hash = has_ref ? buffer.Read<uint32_t>(32) : 0;
|
||||
uint32_t sound_hash = buffer.Read<uint32_t>(32);
|
||||
uint8_t sound_id = buffer.Read<uint8_t>(8);
|
||||
|
||||
bool has_script_hash = buffer.Read<bool>(1);
|
||||
uint32_t script_hash = has_script_hash ? buffer.Read<uint32_t>(32) : 0;
|
||||
|
||||
static const std::unordered_set<uint32_t> blocked_ref_hashes = {"Arena_Vehicle_Mod_Shop_Sounds"_J, "CELEBRATION_SOUNDSET"_J, "DLC_AW_Arena_Office_Planning_Wall_Sounds"_J, "DLC_AW_Arena_Spin_Wheel_Game_Frontend_Sounds"_J, "DLC_Biker_SYG_Sounds"_J, "DLC_BTL_SECURITY_VANS_RADAR_PING_SOUNDS"_J, "DLC_BTL_Target_Pursuit_Sounds"_J, "DLC_GR_Bunker_Door_Sounds"_J, "DLC_GR_CS2_Sounds"_J, "DLC_IO_Warehouse_Mod_Garage_Sounds"_J, "DLC_MPSUM2_HSW_Up_Sounds"_J, "DLC_sum20_Business_Battle_AC_Sounds"_J, "DLC_TG_Running_Back_Sounds"_J, "dlc_vw_table_games_frontend_sounds"_J, "dlc_xm_facility_entry_exit_sounds"_J, "Frontend"_J, "GTAO_Boss_Goons_FM_Soundset"_J, "GTAO_Exec_SecuroServ_Computer_Sounds"_J, "GTAO_Exec_SecuroServ_Warehouse_PC_Sounds"_J, "GTAO_Script_Doors_Faded_Screen_Sounds"_J, "GTAO_SMG_Hangar_Computer_Sounds"_J, "HUD_AMMO_SHOP_SOUNDSET"_J, "HUD_FRONTEND_CUSTOM_SOUNDSET"_J, "HUD_FRONTEND_DEFAULT_SOUNDSET"_J, "HUD_FRONTEND_MP_SOUNDSET"_J, "HUD_FRONTEND_MP_COLLECTABLE_SOUNDS"_J, "HUD_FRONTEND_TATTOO_SHOP_SOUNDSET"_J, "HUD_FRONTEND_CLOTHESSHOP_SOUNDSET"_J, "HUD_FRONTEND_STANDARD_PICKUPS_NPC_SOUNDSET"_J, "HUD_FRONTEND_VEHICLE_PICKUPS_NPC_SOUNDSET"_J, "HUD_FRONTEND_WEAPONS_PICKUPS_NPC_SOUNDSET"_J, "HUD_FREEMODE_SOUNDSET"_J, "HUD_MINI_GAME_SOUNDSET"_J, "HUD_AWARDS"_J, "JA16_Super_Mod_Garage_Sounds"_J, "Low2_Super_Mod_Garage_Sounds"_J, "MissionFailedSounds"_J, "MP_CCTV_SOUNDSET"_J, "MP_LOBBY_SOUNDS"_J, "MP_MISSION_COUNTDOWN_SOUNDSET"_J, "Phone_SoundSet_Default"_J, "Phone_SoundSet_Glasses_Cam"_J, "Phone_SoundSet_Prologue"_J, "Phone_SoundSet_Franklin"_J, "Phone_SoundSet_Michael"_J, "Phone_SoundSet_Trevor"_J, "PLAYER_SWITCH_CUSTOM_SOUNDSET"_J, "RESPAWN_ONLINE_SOUNDSET"_J, "TATTOOIST_SOUNDS"_J, "WastedSounds"_J, "WEB_NAVIGATION_SOUNDS_PHONE"_J};
|
||||
static const std::unordered_set<uint32_t> blocked_sound_hashes = {"Remote_Ring"_J, "COP_HELI_CAM_ZOOM"_J, "Object_Dropped_Remote"_J};
|
||||
if (blocked_ref_hashes.contains(ref_hash) || blocked_sound_hashes.contains(sound_hash))
|
||||
return true;
|
||||
|
||||
switch (sound_hash)
|
||||
{
|
||||
case "DLC_XM_Explosions_Orbital_Cannon"_J:
|
||||
{
|
||||
if (is_entity)
|
||||
return true;
|
||||
|
||||
if (!scr_globals::globalplayer_bd.as<GlobalPlayerBD*>()->Entries[plyr->id()].OrbitalBitset.IsSet(eOrbitalBitset::kOrbitalCannonActive))
|
||||
return true;
|
||||
|
||||
static const std::unordered_set<uint32_t> valid_script_hashes = {"am_mp_defunct_base"_J, "am_mp_orbital_cannon"_J, "fm_mission_controller_2020"_J, "fm_mission_controller"_J};
|
||||
if (!valid_script_hashes.contains(script_hash))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ref_hash)
|
||||
{
|
||||
case "GTAO_Biker_Modes_Soundset"_J:
|
||||
case "DLC_Biker_Sell_Postman_Sounds"_J:
|
||||
{
|
||||
if (is_entity)
|
||||
return true;
|
||||
|
||||
if (script_hash != "gb_biker_contraband_sell"_J)
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case "DLC_AW_General_Sounds"_J:
|
||||
{
|
||||
if (sound_hash != "Airhorn_Blast_Long"_J)
|
||||
return true;
|
||||
|
||||
if (script_hash != "gb_casino_heist"_J)
|
||||
return true;
|
||||
|
||||
if (!gta_util::find_script_thread("gb_casino_heist"_J))
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
case "GTAO_FM_Events_Soundset"_J:
|
||||
{
|
||||
if (!is_entity)
|
||||
return true;
|
||||
|
||||
if (sound_hash != "Explosion_Countdown"_J)
|
||||
return true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Seek(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void hooks::received_event(rage::netEventMgr* event_manager, CNetGamePlayer* source_player, CNetGamePlayer* target_player, uint16_t event_id, int event_index, int event_handled_bitset, int buffer_size, rage::datBitBuffer* buffer)
|
||||
{
|
||||
if (event_id > 91u) [[unlikely]]
|
||||
@ -521,9 +626,7 @@ namespace big
|
||||
// player sending this event is a modder
|
||||
case eNetworkEvents::REPORT_MYSELF_EVENT:
|
||||
{
|
||||
if (auto plyr = g_player_service->get_by_id(source_player->m_player_id))
|
||||
session::add_infraction(plyr, Infraction::TRIGGERED_ANTICHEAT);
|
||||
|
||||
session::add_infraction(plyr, Infraction::TRIGGERED_ANTICHEAT);
|
||||
g.reactions.game_anti_cheat_modder_detection.process(plyr);
|
||||
break;
|
||||
}
|
||||
@ -539,11 +642,11 @@ namespace big
|
||||
|| personal_vehicle == veh //Or we're in our personal vehicle.
|
||||
|| self::spawned_vehicles.contains(net_id)) // Or it's a vehicle we spawned.
|
||||
{
|
||||
auto plyr = g_player_service->get_by_id(source_player->m_player_id);
|
||||
// Let trusted friends and players request control (e.g., they want to hook us to their tow-truck or something)
|
||||
if (plyr && (plyr->is_trusted || (g.session.trust_friends && plyr->is_friend())))
|
||||
{
|
||||
return;
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_local_player->m_vehicle->m_driver != source_player->m_player_info->m_ped) //This will block hackers who are not in the car but still want control.
|
||||
@ -697,39 +800,20 @@ namespace big
|
||||
{
|
||||
if (plyr->m_play_sound_rate_limit.exceeded_last_process())
|
||||
{
|
||||
notify::crash_blocked(source_player, "sound spam");
|
||||
//notify::crash_blocked(source_player, "sound spam"); --- false positives
|
||||
}
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_entity = buffer->Read<bool>(1);
|
||||
std::int16_t entity_net_id;
|
||||
rage::fvector3 position;
|
||||
uint32_t ref_hash;
|
||||
|
||||
if (is_entity)
|
||||
entity_net_id = buffer->Read<std::int16_t>(13);
|
||||
else
|
||||
{
|
||||
position.x = buffer->ReadSignedFloat(19, 1337.0f);
|
||||
position.y = buffer->ReadSignedFloat(19, 1337.0f);
|
||||
position.z = buffer->ReadFloat(19, 1337.0f);
|
||||
}
|
||||
|
||||
bool has_ref = buffer->Read<bool>(1);
|
||||
if (has_ref)
|
||||
ref_hash = buffer->Read<uint32_t>(32);
|
||||
|
||||
uint32_t sound_hash = buffer->Read<uint32_t>(32);
|
||||
|
||||
if (sound_hash == "Remote_Ring"_J && plyr)
|
||||
if (plyr && scan_play_sound_event(plyr, *buffer))
|
||||
{
|
||||
g.reactions.sound_spam.process(plyr);
|
||||
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
case eNetworkEvents::EXPLOSION_EVENT:
|
||||
@ -751,7 +835,59 @@ namespace big
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
case eNetworkEvents::ACTIVATE_VEHICLE_SPECIAL_ABILITY_EVENT:
|
||||
{
|
||||
int16_t net_id = buffer->Read<int16_t>(13);
|
||||
|
||||
if (is_local_vehicle(net_id))
|
||||
{
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
case eNetworkEvents::DOOR_BREAK_EVENT:
|
||||
{
|
||||
int16_t net_id = buffer->Read<int16_t>(13);
|
||||
|
||||
if (is_local_vehicle(net_id))
|
||||
{
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
case eNetworkEvents::CHANGE_RADIO_STATION_EVENT:
|
||||
{
|
||||
int16_t net_id = buffer->Read<int16_t>(13);
|
||||
|
||||
if (is_local_vehicle(net_id))
|
||||
{
|
||||
if (!is_in_vehicle(plyr->get_ped()))
|
||||
{
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plyr->m_radio_station_change_rate_limit.process())
|
||||
{
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pointers->m_gta.m_send_event_ack(event_manager, source_player, target_player, event_index, event_handled_bitset);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->Seek(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return g_hooking->get_original<received_event>()(event_manager, source_player, target_player, event_id, event_index, event_handled_bitset, buffer_size, buffer);
|
||||
|
@ -5,7 +5,7 @@ namespace big
|
||||
{
|
||||
void hooks::searchlight_crash(void* a1, CPed* ped)
|
||||
{
|
||||
if (!ped || !g_pointers->m_gta.m_get_unk_weapon(ped)) [[unlikely]]
|
||||
if (!ped || !g_pointers->m_gta.m_get_searchlight(ped)) [[unlikely]]
|
||||
return;
|
||||
|
||||
return g_hooking->get_original<hooks::searchlight_crash>()(a1, ped);
|
||||
|
@ -31,6 +31,9 @@ namespace big
|
||||
|
||||
m_exception_info = exception_info;
|
||||
|
||||
m_dump.str("");
|
||||
m_dump.clear();
|
||||
|
||||
m_dump << exception_code_to_string(exception_info->ExceptionRecord->ExceptionCode) << '\n';
|
||||
|
||||
if (g.in_script_vm)
|
||||
@ -77,7 +80,7 @@ namespace big
|
||||
{
|
||||
auto mod_info = module_info(table_entry->FullDllName.Buffer, table_entry->DllBase);
|
||||
|
||||
m_dump << mod_info.m_path.filename().string() << " Base Address: " << HEX_TO_UPPER(mod_info.m_base)
|
||||
m_dump << mod_info.m_name << " Base Address: " << HEX_TO_UPPER(mod_info.m_base)
|
||||
<< " Size: " << mod_info.m_size << '\n';
|
||||
|
||||
m_modules.emplace_back(std::move(mod_info));
|
||||
@ -128,29 +131,39 @@ namespace big
|
||||
|
||||
for (size_t i = 0; i < m_frame_pointers.size() && m_frame_pointers[i]; ++i)
|
||||
{
|
||||
const auto addr = m_frame_pointers[i];
|
||||
const auto addr = m_frame_pointers[i];
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
|
||||
m_dump << "\n[" << i << "]\t";
|
||||
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol))
|
||||
{
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line))
|
||||
{
|
||||
m_dump << line.FileName << " L: " << line.LineNumber << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
m_dump << line.FileName << " L: " << line.LineNumber << ' ' << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
|
||||
if (module_info)
|
||||
{
|
||||
m_dump << module_info->m_name << ' ' << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
if (module_info->m_base == (uint64_t)GetModuleHandle(0))
|
||||
m_dump << module_info->m_path.filename().string() << " " << std::string_view(symbol->Name, symbol->NameLen) << " ("
|
||||
<< module_info->m_path.filename().string() << "+" << HEX_TO_UPPER(addr - module_info->m_base) << ")";
|
||||
else
|
||||
m_dump << module_info->m_path.filename().string() << " " << std::string_view(symbol->Name, symbol->NameLen);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_dump << HEX_TO_UPPER(addr) << ' ' << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
const auto module_info = get_module_by_address(addr);
|
||||
m_dump << module_info->m_path.filename().string() << "+" << HEX_TO_UPPER(addr - module_info->m_base) << " " << HEX_TO_UPPER(addr);
|
||||
|
||||
if (module_info)
|
||||
{
|
||||
m_dump << module_info->m_name << '+' << HEX_TO_UPPER(addr - module_info->m_base) << ' ' << HEX_TO_UPPER(addr);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_dump << HEX_TO_UPPER(addr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +180,7 @@ namespace big
|
||||
if (m_exception_info->ExceptionRecord->ExceptionCode == msvc_exception_code)
|
||||
{
|
||||
m_dump
|
||||
<< '\n'
|
||||
<< reinterpret_cast<const std::exception*>(m_exception_info->ExceptionRecord->ExceptionInformation[1])->what() << '\n';
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace big
|
||||
struct module_info
|
||||
{
|
||||
module_info(std::filesystem::path path, void* base) :
|
||||
m_path(path),
|
||||
m_name(path.filename().string()),
|
||||
m_base(reinterpret_cast<uintptr_t>(base))
|
||||
{
|
||||
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
|
||||
@ -29,7 +29,7 @@ namespace big
|
||||
m_size = nt_header->OptionalHeader.SizeOfCode;
|
||||
}
|
||||
|
||||
std::filesystem::path m_path;
|
||||
std::string m_name;
|
||||
uintptr_t m_base;
|
||||
size_t m_size;
|
||||
};
|
||||
|
@ -545,15 +545,6 @@ namespace big
|
||||
g_pointers->m_gta.m_fipackfile_mount = ptr.add(0x47).rip().as<functions::fipackfile_mount>();
|
||||
}
|
||||
},
|
||||
// Invalid Mods Crash Detour
|
||||
{
|
||||
"IMCD",
|
||||
"E8 ? ? ? ? 40 88 7C 24 ? 49 89 9C 24",
|
||||
[](memory::handle ptr)
|
||||
{
|
||||
g_pointers->m_gta.m_invalid_mods_crash_detour = ptr.add(1).rip().as<PVOID>();
|
||||
}
|
||||
},
|
||||
// Send Chat Ptr
|
||||
{
|
||||
"SCP",
|
||||
@ -1612,10 +1603,12 @@ namespace big
|
||||
// ERROR message box
|
||||
{
|
||||
"E0MB",
|
||||
"E8 ? ? ? ? CC FF 15",
|
||||
"E8 ? ? ? ? 33 F6 EB 0F",
|
||||
[](memory::handle ptr)
|
||||
{
|
||||
g_pointers->m_gta.m_error_message_box = ptr.add(1).rip().as<PVOID>();
|
||||
ptr = ptr.add(1).rip();
|
||||
g_pointers->m_gta.m_error_message_box = ptr.add(7).rip().as<PVOID>();
|
||||
g_pointers->m_gta.m_error_message_box_2 = ptr.as<PVOID>();
|
||||
}
|
||||
},
|
||||
// Get title caption for ERROR message box
|
||||
@ -1772,16 +1765,16 @@ namespace big
|
||||
[](memory::handle ptr)
|
||||
{
|
||||
g_pointers->m_gta.m_searchlight_crash = ptr.sub(0x1E).as<PVOID>();
|
||||
g_pointers->m_gta.m_get_unk_weapon = ptr.add(0x28).rip().as<functions::get_unk_weapon>();
|
||||
g_pointers->m_gta.m_get_searchlight = ptr.add(0x28).rip().as<functions::get_searchlight>();
|
||||
}
|
||||
},
|
||||
// Clone Create Pool
|
||||
// Vehicle Allocator
|
||||
{
|
||||
"CCP",
|
||||
"VA",
|
||||
"48 8B 0D ? ? ? ? 45 33 C9 BA ? ? ? ? 41",
|
||||
[](memory::handle ptr)
|
||||
{
|
||||
g_pointers->m_gta.m_clone_create_pool = ptr.add(3).rip().as<GenericPool**>();
|
||||
g_pointers->m_gta.m_vehicle_allocator = ptr.add(3).rip().as<GenericPool**>();
|
||||
}
|
||||
},
|
||||
// Write Physical Script Game State Data Node
|
||||
|
@ -70,6 +70,7 @@ namespace big
|
||||
rate_limiter m_play_sound_rate_limit_tse{5s, 2};
|
||||
rate_limiter m_invites_rate_limit{10s, 2};
|
||||
rate_limiter m_radio_request_rate_limit{5s, 2};
|
||||
rate_limiter m_radio_station_change_rate_limit{1s, 3};
|
||||
|
||||
bool block_radio_requests = false;
|
||||
|
||||
|
@ -1,36 +1,14 @@
|
||||
#pragma once
|
||||
#include "pointers.hpp"
|
||||
#include "sync_trees.hpp"
|
||||
#include "util/math.hpp"
|
||||
#include "util/model_info.hpp"
|
||||
#include "util/pools.hpp"
|
||||
|
||||
#include <entities/CDynamicEntity.hpp>
|
||||
#include <random>
|
||||
|
||||
namespace big::fuzzer
|
||||
{
|
||||
// helpers
|
||||
|
||||
// [0, max_value)
|
||||
inline int rand(int max_value)
|
||||
{
|
||||
std::random_device seed;
|
||||
std::mt19937 gen{seed()};
|
||||
std::uniform_int_distribution<int> dist{0, max_value - 1};
|
||||
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
// [min_value, max_value]
|
||||
inline int rand(int min_value, int max_value)
|
||||
{
|
||||
std::random_device seed;
|
||||
std::mt19937 gen{seed()};
|
||||
std::uniform_int_distribution<int> dist{min_value, max_value};
|
||||
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
inline bool is_fuzzer_enabled()
|
||||
{
|
||||
return g.debug.fuzzer.active && g.debug.fuzzer.enabled && g.debug.fuzzer.thread_id == GetCurrentThreadId();
|
||||
@ -86,7 +64,7 @@ namespace big::fuzzer
|
||||
else if (info && (info->m_model_type == eModelType::Ped || info->m_model_type == eModelType::OnlineOnlyPed))
|
||||
std::erase(models, "player_zero"_J);
|
||||
|
||||
return models[rand(models.size())];
|
||||
return models[math::rand(models.size())];
|
||||
}
|
||||
|
||||
inline rage::joaat_t get_crash_model(rage::joaat_t original)
|
||||
@ -100,7 +78,7 @@ namespace big::fuzzer
|
||||
else if (info && info->m_model_type == eModelType::Vehicle)
|
||||
return "arbitergt"_J;
|
||||
else
|
||||
return rand(2) ? "urbanweeds01"_J : "slod_human"_J;
|
||||
return math::rand(2) ? "urbanweeds01"_J : "slod_human"_J;
|
||||
}
|
||||
|
||||
inline std::int16_t get_first_ped_id()
|
||||
@ -147,18 +125,18 @@ namespace big::fuzzer
|
||||
return -1;
|
||||
|
||||
if (is_object_model(entity->m_model_info->m_hash))
|
||||
return rand(2) ? get_first_ped_id() : get_first_veh_id();
|
||||
return math::rand(2) ? get_first_ped_id() : get_first_veh_id();
|
||||
else if (entity->m_model_info->m_model_type == eModelType::Ped || entity->m_model_info->m_model_type == eModelType::OnlineOnlyPed)
|
||||
return rand(2) ? get_first_obj_id() : get_first_veh_id();
|
||||
return math::rand(2) ? get_first_obj_id() : get_first_veh_id();
|
||||
else if (entity->m_model_info->m_model_type == eModelType::Vehicle)
|
||||
return rand(2) ? get_first_obj_id() : get_first_ped_id();
|
||||
return math::rand(2) ? get_first_obj_id() : get_first_ped_id();
|
||||
|
||||
return rand(2) ? get_first_ped_id() : get_first_veh_id();
|
||||
return math::rand(2) ? get_first_ped_id() : get_first_veh_id();
|
||||
}
|
||||
|
||||
inline rage::fvector3 get_fuzz_vector()
|
||||
{
|
||||
if (rand(2) == 0)
|
||||
if (math::rand(2) == 0)
|
||||
{
|
||||
return {10800.0f, 10800.0f, 10.0f}; // host crash coords
|
||||
}
|
||||
@ -233,7 +211,7 @@ namespace big::fuzzer
|
||||
if (!is_fuzzer_enabled())
|
||||
return net_id;
|
||||
|
||||
int option = rand(0, 3);
|
||||
int option = math::rand(0, 3);
|
||||
|
||||
switch (option)
|
||||
{
|
||||
@ -269,7 +247,7 @@ namespace big::fuzzer
|
||||
|
||||
if (hash == 0 || is_model_hash(hash))
|
||||
{
|
||||
int option = rand(0, 4);
|
||||
int option = math::rand(0, 4);
|
||||
|
||||
switch (option)
|
||||
{
|
||||
@ -282,8 +260,8 @@ namespace big::fuzzer
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rand(4))
|
||||
hash = rand(0, UINT_MAX); // not much we can do here
|
||||
if (math::rand(4))
|
||||
hash = math::rand(0, UINT_MAX); // not much we can do here
|
||||
}
|
||||
|
||||
return hash;
|
||||
@ -294,7 +272,7 @@ namespace big::fuzzer
|
||||
if (!is_fuzzer_enabled())
|
||||
return value;
|
||||
|
||||
if (rand(4) == 0)
|
||||
if (math::rand(4) == 0)
|
||||
return value; // flip bools
|
||||
else
|
||||
return true; // set to true
|
||||
@ -311,7 +289,7 @@ namespace big::fuzzer
|
||||
return fuzz_potential_hash(bits);
|
||||
else
|
||||
{
|
||||
if (rand(5 - (count == 1)) == 0)
|
||||
if (math::rand(5 - (count == 1)) == 0)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
@ -319,8 +297,8 @@ namespace big::fuzzer
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rand(2) == 0)
|
||||
return rand(((int)pow(2, count))); // random
|
||||
if (math::rand(2) == 0)
|
||||
return math::rand(((int)pow(2, count))); // random
|
||||
else
|
||||
return ((int)pow(2, count)) - 1; // max possible value
|
||||
}
|
||||
@ -334,7 +312,7 @@ namespace big::fuzzer
|
||||
{
|
||||
auto n = fuzz_bits(bits, count - 1);
|
||||
|
||||
if (rand(5) == 0)
|
||||
if (math::rand(5) == 0)
|
||||
n = -n;
|
||||
|
||||
return n;
|
||||
@ -348,7 +326,7 @@ namespace big::fuzzer
|
||||
// well not much to do here I suppose
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
*(char*)((__int64)data + i) = rand(0, 255);
|
||||
*(char*)((__int64)data + i) = math::rand(0, 255);
|
||||
}
|
||||
|
||||
inline float fuzz_float(float orig, int size, float divisor, bool _signed)
|
||||
@ -358,9 +336,9 @@ namespace big::fuzzer
|
||||
if (!is_fuzzer_enabled())
|
||||
return orig;
|
||||
|
||||
if (rand(3) == 0)
|
||||
if (math::rand(3) == 0)
|
||||
{
|
||||
int option = rand(0, 2);
|
||||
int option = math::rand(0, 2);
|
||||
switch (option)
|
||||
{
|
||||
case 0: return truncate_func(9999.9f, size, divisor);
|
||||
@ -376,7 +354,7 @@ namespace big::fuzzer
|
||||
{
|
||||
auto truncate_func = _signed_z ? &truncate_vector_signed_z : &truncate_vector;
|
||||
|
||||
if (rand(3) == 0)
|
||||
if (math::rand(3) == 0)
|
||||
{
|
||||
return truncate_func(get_fuzz_vector(), size, divisor);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "pointers.hpp"
|
||||
#include "natives.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace big::math
|
||||
{
|
||||
inline float deg_to_rad(float deg)
|
||||
@ -63,4 +65,24 @@ namespace big::math
|
||||
SHAPETEST::GET_SHAPE_TEST_RESULT(ray, &hit, &end_coords, &surface_normal, &hit_entity);
|
||||
return end_coords;
|
||||
}
|
||||
|
||||
// [0, max_value)
|
||||
inline int rand(int max_value)
|
||||
{
|
||||
static std::random_device seed;
|
||||
static std::mt19937 gen{seed()};
|
||||
std::uniform_int_distribution<int> dist{0, max_value - 1};
|
||||
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
// [min_value, max_value]
|
||||
inline int rand(int min_value, int max_value)
|
||||
{
|
||||
static std::random_device seed;
|
||||
static std::mt19937 gen{seed()};
|
||||
std::uniform_int_distribution<int> dist{min_value, max_value};
|
||||
|
||||
return dist(gen);
|
||||
}
|
||||
}
|
||||
|
@ -4,42 +4,39 @@
|
||||
|
||||
namespace big::protection
|
||||
{
|
||||
constexpr auto crash_objects = {"proc_drygrasses01"_J, "proc_drygrasses01b"_J, "proc_brittlebush_01"_J, "proc_forest_ivy_01"_J, "proc_grassdandelion01"_J, "proc_grasses01b"_J, "proc_grassfronds01"_J, "proc_grassplantmix_01"_J, "proc_indian_pbrush_01"_J, "proc_leafybush_01"_J, "proc_lizardtail_01"_J, "proc_lupins_01"_J, "proc_meadowmix_01"_J, "proc_meadowpoppy_01"_J, "proc_desert_sage_01"_J, "prop_saplin_001_b"_J, "prop_dummy_01"_J, "prop_dummy_car"_J, "prop_dummy_light"_J, "prop_dummy_plane"_J, "prop_distantcar_night"_J, "prop_distantcar_day"_J, "hei_bh1_08_details4_em_night"_J, "dt1_18_sq_night_slod"_J, "ss1_12_night_slod"_J, "hash_b334b5e2_qyquzxq_collision"_J, "h4_prop_bush_bgnvla_med_01"_J, "h4_prop_bush_bgnvla_lrg_01"_J, "h4_prop_bush_buddleia_low_01"_J, "h4_prop_bush_ear_aa"_J, "h4_prop_bush_ear_ab"_J, "h4_prop_bush_fern_low_01"_J, "h4_prop_bush_fern_tall_cc"_J, "h4_prop_bush_mang_ad"_J, "h4_prop_bush_mang_low_aa"_J, "h4_prop_bush_mang_low_ab"_J, "h4_prop_bush_seagrape_low_01"_J, "prop_h4_ground_cover"_J, "h4_prop_weed_groundcover_01"_J, "h4_prop_grass_med_01"_J, "h4_prop_grass_tropical_lush_01"_J, "h4_prop_grass_wiregrass_01"_J, "h4_prop_weed_01_plant"_J, "h4_prop_weed_01_row"_J, "urbanweeds02_l1"_J, "proc_forest_grass01"_J, "prop_small_bushyba"_J, "v_res_d_dildo_a"_J, "v_res_d_dildo_b"_J, "v_res_d_dildo_c"_J, "v_res_d_dildo_d"_J, "v_res_d_dildo_e"_J, "v_res_d_dildo_f"_J, "v_res_skateboard"_J, "prop_battery_01"_J, "prop_barbell_01"_J, "prop_barbell_02"_J, "prop_bandsaw_01"_J, "prop_bbq_3"_J, "v_med_curtainsnewcloth2"_J, "bh1_07_flagpoles"_J, "hash_058a7eb5_deihiws_collision"_J, "proc_dry_plants_01"_J, "proc_leafyplant_01"_J, "proc_grassplantmix_02"_J, "proc_dryplantsgrass_01"_J, "proc_dryplantsgrass_02"_J, "proc_grasses01"_J, "prop_dryweed_002_a"_J, "prop_fernba"_J, "prop_weed_001_aa"_J, "urbangrnfrnds_01"_J, "urbanweeds01"_J, "prop_dandy_b"_J, "v_proc2_temp"_J, "prop_fernbb"_J, "proc_drygrassfronds01"_J, "prop_log_ae"_J, "prop_grass_da"_J, "prop_fragtest_cnst_04"_J};
|
||||
static const std::unordered_set<uint32_t> crash_objects = {"proc_drygrasses01"_J, "proc_drygrasses01b"_J, "proc_brittlebush_01"_J, "proc_forest_ivy_01"_J, "proc_grassdandelion01"_J, "proc_grasses01b"_J, "proc_grassfronds01"_J, "proc_grassplantmix_01"_J, "proc_indian_pbrush_01"_J, "proc_leafybush_01"_J, "proc_lizardtail_01"_J, "proc_lupins_01"_J, "proc_meadowmix_01"_J, "proc_meadowpoppy_01"_J, "proc_desert_sage_01"_J, "prop_saplin_001_b"_J, "prop_dummy_01"_J, "prop_dummy_car"_J, "prop_dummy_light"_J, "prop_dummy_plane"_J, "prop_distantcar_night"_J, "prop_distantcar_day"_J, "hei_bh1_08_details4_em_night"_J, "dt1_18_sq_night_slod"_J, "ss1_12_night_slod"_J, "hash_b334b5e2_qyquzxq_collision"_J, "h4_prop_bush_bgnvla_med_01"_J, "h4_prop_bush_bgnvla_lrg_01"_J, "h4_prop_bush_buddleia_low_01"_J, "h4_prop_bush_ear_aa"_J, "h4_prop_bush_ear_ab"_J, "h4_prop_bush_fern_low_01"_J, "h4_prop_bush_fern_tall_cc"_J, "h4_prop_bush_mang_ad"_J, "h4_prop_bush_mang_low_aa"_J, "h4_prop_bush_mang_low_ab"_J, "h4_prop_bush_seagrape_low_01"_J, "prop_h4_ground_cover"_J, "h4_prop_weed_groundcover_01"_J, "h4_prop_grass_med_01"_J, "h4_prop_grass_tropical_lush_01"_J, "h4_prop_grass_wiregrass_01"_J, "h4_prop_weed_01_plant"_J, "h4_prop_weed_01_row"_J, "urbanweeds02_l1"_J, "proc_forest_grass01"_J, "prop_small_bushyba"_J, "v_res_d_dildo_a"_J, "v_res_d_dildo_b"_J, "v_res_d_dildo_c"_J, "v_res_d_dildo_d"_J, "v_res_d_dildo_e"_J, "v_res_d_dildo_f"_J, "v_res_skateboard"_J, "prop_battery_01"_J, "prop_barbell_01"_J, "prop_barbell_02"_J, "prop_bandsaw_01"_J, "prop_bbq_3"_J, "v_med_curtainsnewcloth2"_J, "bh1_07_flagpoles"_J, "hash_058a7eb5_deihiws_collision"_J, "proc_dry_plants_01"_J, "proc_leafyplant_01"_J, "proc_grassplantmix_02"_J, "proc_dryplantsgrass_01"_J, "proc_dryplantsgrass_02"_J, "proc_grasses01"_J, "prop_dryweed_002_a"_J, "prop_fernba"_J, "prop_weed_001_aa"_J, "urbangrnfrnds_01"_J, "urbanweeds01"_J, "prop_dandy_b"_J, "v_proc2_temp"_J, "prop_fernbb"_J, "proc_drygrassfronds01"_J, "prop_log_ae"_J, "prop_grass_da"_J, "prop_fragtest_cnst_04"_J, "prop_thindesertfiller_aa"_J};
|
||||
bool is_crash_object(rage::joaat_t model)
|
||||
{
|
||||
if (crash_objects.contains(model))
|
||||
return true;
|
||||
if (!model_info::get_model(model))
|
||||
return false;
|
||||
if (!model_info::is_model_of_type(model, eModelType::Object, eModelType::Time, eModelType::Weapon, eModelType::Destructable, eModelType::WorldObject, eModelType::Sprinkler, eModelType::Unk65, eModelType::Plant, eModelType::LOD, eModelType::Unk132, eModelType::Building))
|
||||
return true;
|
||||
for (auto iterator : crash_objects)
|
||||
if (iterator == model)
|
||||
return true;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr auto crash_peds = {"slod_human"_J, "slod_small_quadped"_J, "slod_large_quadped"_J};
|
||||
static const std::unordered_set<uint32_t> crash_peds = {"slod_human"_J, "slod_small_quadped"_J, "slod_large_quadped"_J};
|
||||
bool is_crash_ped(rage::joaat_t model)
|
||||
{
|
||||
for (auto iterator : crash_peds)
|
||||
if (iterator == model)
|
||||
return true;
|
||||
if (crash_peds.contains(model))
|
||||
return true;
|
||||
if (!model_info::is_model_of_type(model, eModelType::Ped, eModelType::OnlineOnlyPed))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr auto crash_vehicles = {"arbitergt"_J, "astron2"_J, "cyclone2"_J, "ignus2"_J, "s95"_J};
|
||||
static const std::unordered_set<uint32_t> crash_vehicles = {"arbitergt"_J, "astron2"_J, "cyclone2"_J, "ignus2"_J, "s95"_J};
|
||||
bool is_crash_vehicle(rage::joaat_t model)
|
||||
{
|
||||
for (auto iterator : crash_vehicles)
|
||||
if (iterator == model)
|
||||
return true;
|
||||
if (crash_vehicles.contains(model))
|
||||
return true;
|
||||
if (!model_info::is_model_of_type(model, eModelType::Vehicle, eModelType::Unk133))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr auto valid_player_models = {
|
||||
static const std::unordered_set<uint32_t> valid_player_models = {
|
||||
"mp_m_freemode_01"_J,
|
||||
"mp_f_freemode_01"_J,
|
||||
"u_m_m_filmdirector"_J,
|
||||
@ -80,9 +77,6 @@ namespace big::protection
|
||||
};
|
||||
bool is_valid_player_model(rage::joaat_t model)
|
||||
{
|
||||
for (auto iterator : valid_player_models)
|
||||
if (iterator == model)
|
||||
return true;
|
||||
return false;
|
||||
return valid_player_models.contains(model);
|
||||
}
|
||||
}
|
||||
|
@ -534,9 +534,6 @@ namespace big
|
||||
{
|
||||
for (int i = (int)eNetObjType::NET_OBJ_TYPE_AUTOMOBILE; i <= (int)eNetObjType::NET_OBJ_TYPE_TRAIN; i++)
|
||||
{
|
||||
if (i == (int)eNetObjType::NET_OBJ_TYPE_TRAILER)
|
||||
continue;
|
||||
|
||||
rage::netSyncTree* tree = g_pointers->m_gta.m_get_sync_tree_for_type(*g_pointers->m_gta.m_network_object_mgr, i);
|
||||
|
||||
if (tree->m_child_node_count != finder.sync_trees_node_array_index_to_node_id[i].size())
|
||||
|
@ -115,6 +115,11 @@ namespace ImGui
|
||||
true,
|
||||
style.FrameRounding);
|
||||
|
||||
if (*k < 0 || *k > VK_RMENU)
|
||||
{
|
||||
*k = 0;
|
||||
}
|
||||
|
||||
if (*k != 0 && g.ActiveId != id)
|
||||
{
|
||||
strcpy_s(buf_display, key_names[*k]);
|
||||
|
Loading…
Reference in New Issue
Block a user