// menu.cpp — feature state, config, overlay draw + ISurface discovery #include "hl2h.h" #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // ConMsg (tier0 console printer — prints to in-game console) // --------------------------------------------------------------------------- void (*g_conmsg)(const char *fmt, ...) = nullptr; void init_conmsg() { g_conmsg = (decltype(g_conmsg))dlsym(RTLD_DEFAULT, "_Z6ConMsgPKcz"); if (g_conmsg) return; // fallback: dlopen libtier0.so from the engine directory size_t slash = g_engine.path.find_last_of('/'); if (slash == std::string::npos) return; std::string tier0 = g_engine.path.substr(0, slash + 1) + "libtier0.so"; void *h = dlopen(tier0.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); if (!h) h = dlopen(tier0.c_str(), RTLD_LAZY | RTLD_GLOBAL); if (h) g_conmsg = (decltype(g_conmsg))dlsym(h, "_Z6ConMsgPKcz"); } // feature data (declared in hl2h.h; shared with the GUI) FeatureState g_bhop{true, 0}; bool g_bhop_autostrafe = true; FeatureState g_abh{true, 0}; FeatureState g_jumpbug{true, 0}; bool g_jumpbug_assist = false; FeatureState g_melee{true, 0}; FeatureState g_spread{true, 0}; bool g_spread_no_spread = true; FeatureState g_esp{true, 0}; FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in the GUI FeatureState g_triggerbot{true, 603}; // default Mouse4 FeatureState g_silentaim{true, 602}; // default Mouse3 FeatureState g_item_esp{true, 0}; // valuable item tags, always-on by default FeatureState g_fullbright{false, 0}; // mat_fullbright world lighting FeatureState g_world_tint{false, 0}; // material colour modulation bool g_tint_world_on = true; bool g_tint_sky_on = true; bool g_tint_prop_on = true; int g_tint_world_rgb[3] = {255, 255, 255}; int g_tint_sky_rgb[3] = {255, 255, 255}; int g_tint_prop_rgb[3] = {255, 255, 255}; FeatureState g_fog{false, 0}; // fog_override colour/distance bool g_fog_skybox = true; bool g_fog_disable = false; int g_fog_rgb[3] = {160, 170, 190}; int g_fog_start = 200; int g_fog_end = 3000; FeatureState g_skybox{false, 0}; // skybox.png image override bool g_item_health = true; bool g_item_ammo = true; bool g_item_armor = true; bool g_item_weapons = true; bool g_item_crates = true; bool g_item_distance = true; int g_item_max_dist = 5000; // Aimbot bone checklist. Head defaults on (v1 behaviour). bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, false, false, false}; int g_aim_smooth = 5; // degrees per tick bool g_aim_vischeck = true; int g_aim_fov = 60; // angular distance from the crosshair bool g_aim_fov_circle = true; bool g_aim_punch = true; // compensate server aim punch bool g_spread_engine = true; // zero the pellet random draws, not just the seed bool g_auto_crouchjump = false; bool g_edge_jump = false; int g_fov_camera = 75; // HL2's stock camera FOV int g_fov_viewmodel = 54; // HL2's stock viewmodel FOV int g_tonemap = 0; // mat_force_tonemap_scale x10, 0 = automatic int g_esp_col_hostile[3] = {255, 60, 60}; int g_esp_col_neutral[3] = {220, 220, 220}; int g_esp_col_friendly[3] = {60, 255, 60}; // ESP sub-options. Defaults match the pre-option behaviour: everything on. bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true, true, true, true, true}; // --------------------------------------------------------------------------- // config persistence (INI format) // --------------------------------------------------------------------------- static void mkdir_p(const std::string &path) { for (size_t i = 1; i <= path.size(); i++) { if (i != path.size() && path[i] != '/') continue; std::string p = path.substr(0, i); if (!p.empty() && p != "/") mkdir(p.c_str(), 0755); } } static std::string config_path() { const char *xdg = getenv("XDG_CONFIG_HOME"); std::string base; if (xdg && *xdg) { base = xdg; } else { const char *home = getenv("HOME"); base = (home && *home) ? std::string(home) + "/.config" : "."; } std::string dir = base + "/hl2hake"; mkdir_p(dir); return dir + "/config"; } // Register the bundled Liberation Sans with fontconfig so the VGUI overlay // does not depend on the host/game install having it. The font file lives in // assets/ next to the .so (or the system path as a fallback). void register_bundled_font() { static bool done = false; if (done) return; done = true; std::string path; Dl_info info; if (dladdr((void *)®ister_bundled_font, &info) && info.dli_fname) { char resolved[4096]; const char *lib = realpath(info.dli_fname, resolved); std::string p = lib ? resolved : info.dli_fname; size_t slash = p.find_last_of('/'); if (slash != std::string::npos) p.resize(slash + 1); else p = "./"; p += "assets/LiberationSans-Regular.ttf"; if (access(p.c_str(), R_OK) == 0) path = p; } if (path.empty()) { const char *sys = "/usr/share/fonts/liberation/LiberationSans-Regular.ttf"; if (access(sys, R_OK) == 0) path = sys; } if (path.empty()) { logf("font: bundled LiberationSans-Regular.ttf not found"); toastf(TOAST_WARN, "Bundled font missing; overlay may not render"); return; } void *fc = dlopen("libfontconfig.so.1", RTLD_LAZY | RTLD_GLOBAL); if (!fc) { logf("font: libfontconfig.so.1 not available"); toastf(TOAST_WARN, "fontconfig unavailable; overlay font not registered"); return; } using InitFn = int (*)(); using GetCurrentFn = void *(*)(); using AddFileFn = int (*)(void *, const char *); auto init = (InitFn)dlsym(fc, "FcInit"); auto get = (GetCurrentFn)dlsym(fc, "FcConfigGetCurrent"); auto add = (AddFileFn)dlsym(fc, "FcConfigAppFontAddFile"); if (!init || !get || !add) { logf("font: fontconfig symbols missing"); toastf(TOAST_WARN, "fontconfig symbols missing; overlay font not registered"); return; } init(); void *cfg = get(); if (cfg && add(cfg, path.c_str())) logf("font: registered %s", path.c_str()); else { logf("font: failed to register %s", path.c_str()); toastf(TOAST_WARN, "Could not register the overlay font"); } } void config_save() { std::string path = config_path(); std::string tmp = path + ".tmp"; FILE *f = fopen(tmp.c_str(), "w"); if (!f) { toastf(TOAST_ERROR, "Config save failed: %s", path.c_str()); return; } fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, g_bhop.bind, g_bhop_autostrafe); fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); fprintf(f, "[jumpbug]\nactive=%d\nbind=%d\nassist=%d\n", g_jumpbug.active, g_jumpbug.bind, (int)g_jumpbug_assist); fprintf(f, "[melee]\nactive=%d\nbind=%d\n", g_melee.active, g_melee.bind); fprintf(f, "[spread]\nactive=%d\nbind=%d\nno_spread=%d\nengine=%d\n", g_spread.active, g_spread.bind, (int)g_spread_no_spread, (int)g_spread_engine); fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" "fov=%d\nfov_circle=%d\npunch=%d\n" "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" "bone_lleg=%d\nbone_rleg=%d\n", g_aimbot.active, g_aimbot.bind, g_aim_smooth, (int)g_aim_vischeck, g_aim_fov, (int)g_aim_fov_circle, (int)g_aim_punch, g_aim_bones[AIM_BONE_HEAD], g_aim_bones[AIM_BONE_TORSO], g_aim_bones[AIM_BONE_LARM], g_aim_bones[AIM_BONE_RARM], g_aim_bones[AIM_BONE_LLEG], g_aim_bones[AIM_BONE_RLEG]); fprintf(f, "[triggerbot]\nactive=%d\nbind=%d\n", g_triggerbot.active, g_triggerbot.bind); fprintf(f, "[silentaim]\nactive=%d\nbind=%d\n", g_silentaim.active, g_silentaim.bind); fprintf(f, "[fullbright]\nactive=%d\nbind=%d\n", g_fullbright.active, g_fullbright.bind); fprintf(f, "[worldtint]\nactive=%d\nbind=%d\nworld=%d\nsky=%d\n" "prop=%d\nwr=%d\nwg=%d\nwb=%d\nsr=%d\nsg=%d\nsb=%d\n" "pr=%d\npg=%d\npb=%d\n", g_world_tint.active, g_world_tint.bind, (int)g_tint_world_on, (int)g_tint_sky_on, (int)g_tint_prop_on, g_tint_world_rgb[0], g_tint_world_rgb[1], g_tint_world_rgb[2], g_tint_sky_rgb[0], g_tint_sky_rgb[1], g_tint_sky_rgb[2], g_tint_prop_rgb[0], g_tint_prop_rgb[1], g_tint_prop_rgb[2]); fprintf(f, "[fog]\nactive=%d\nbind=%d\nr=%d\ng=%d\nb=%d\n" "start=%d\nend=%d\nskybox=%d\ndisable=%d\n", g_fog.active, g_fog.bind, g_fog_rgb[0], g_fog_rgb[1], g_fog_rgb[2], g_fog_start, g_fog_end, (int)g_fog_skybox, (int)g_fog_disable); fprintf(f, "[skybox]\nactive=%d\nbind=%d\n", g_skybox.active, g_skybox.bind); fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", g_item_esp.active, g_item_esp.bind, (int)g_item_health, (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, (int)g_item_crates, (int)g_item_distance, g_item_max_dist); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n" "dmg=%d\nalert=%d\nweapon=%d\narrows=%d\n" "hr=%d\nhg=%d\nhb=%d\nnr=%d\nng=%d\nnb=%d\n" "fr=%d\nfg=%d\nfb=%d\n", g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH], g_esp_opt[ESP_OPT_DAMAGE_TAGS], g_esp_opt[ESP_OPT_ALERT], g_esp_opt[ESP_OPT_WEAPON], g_esp_opt[ESP_OPT_ARROWS], g_esp_col_hostile[0], g_esp_col_hostile[1], g_esp_col_hostile[2], g_esp_col_neutral[0], g_esp_col_neutral[1], g_esp_col_neutral[2], g_esp_col_friendly[0], g_esp_col_friendly[1], g_esp_col_friendly[2]); fprintf(f, "[view]\nfov=%d\nvmfov=%d\ntonemap=%d\n", g_fov_camera, g_fov_viewmodel, g_tonemap); fprintf(f, "[movement]\ncrouchjump=%d\nedgejump=%d\n", (int)g_auto_crouchjump, (int)g_edge_jump); gui_config_write(f); bool ok = ferror(f) == 0; if (fflush(f) != 0) ok = false; if (ok) (void)fsync(fileno(f)); // best effort; the rename is what matters if (fclose(f) != 0) ok = false; if (!ok || rename(tmp.c_str(), path.c_str()) != 0) { remove(tmp.c_str()); toastf(TOAST_ERROR, "Config save failed: %s", path.c_str()); } } static bool config_load() { FILE *f = fopen(config_path().c_str(), "r"); if (!f) return false; char line[256]; FeatureState *cur = nullptr; bool in_esp = false, in_bhop = false, in_aim = false, in_jumpbug = false, in_spread = false, in_items = false, in_tint = false, in_fog = false, in_view = false, in_movement = false; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; if (line[0] == '[') { if (strstr(line, "[bhop]")) cur = &g_bhop; else if (strstr(line, "[abh]")) cur = &g_abh; else if (strstr(line, "[jumpbug]")) cur = &g_jumpbug; else if (strstr(line, "[melee]")) cur = &g_melee; else if (strstr(line, "[spread]")) cur = &g_spread; else if (strstr(line, "[esp]")) cur = &g_esp; else if (strstr(line, "[items]")) cur = &g_item_esp; else if (strstr(line, "[fullbright]")) cur = &g_fullbright; else if (strstr(line, "[worldtint]")) cur = &g_world_tint; else if (strstr(line, "[fog]")) cur = &g_fog; else if (strstr(line, "[skybox]")) cur = &g_skybox; else if (strstr(line, "[aimbot]")) cur = &g_aimbot; else if (strstr(line, "[triggerbot]")) cur = &g_triggerbot; else if (strstr(line, "[silentaim]")) cur = &g_silentaim; else if (strstr(line, "[view]")) cur = nullptr; else if (strstr(line, "[movement]")) cur = nullptr; else cur = nullptr; in_esp = (cur == &g_esp); in_bhop = (cur == &g_bhop); in_aim = (cur == &g_aimbot); in_jumpbug = (cur == &g_jumpbug); in_spread = (cur == &g_spread); in_items = (cur == &g_item_esp); in_tint = (cur == &g_world_tint); in_fog = (cur == &g_fog); in_view = strstr(line, "[view]") != nullptr; in_movement = strstr(line, "[movement]") != nullptr; } else if (cur || in_view || in_movement) { char *eq = strchr(line, '='); if (!eq) continue; *eq = 0; const char *key = line, *val = eq + 1; if (in_bhop && strcmp(key, "autostrafe") == 0) { g_bhop_autostrafe = atoi(val) != 0; } if (in_jumpbug && strcmp(key, "assist") == 0) { g_jumpbug_assist = atoi(val) != 0; } if (in_spread) { if (strcmp(key, "no_spread") == 0) g_spread_no_spread = atoi(val) != 0; else if (strcmp(key, "engine") == 0) g_spread_engine = atoi(val) != 0; } if (in_items) { if (strcmp(key, "health") == 0) g_item_health = atoi(val) != 0; else if (strcmp(key, "ammo") == 0) g_item_ammo = atoi(val) != 0; else if (strcmp(key, "armor") == 0) g_item_armor = atoi(val) != 0; else if (strcmp(key, "weapons") == 0) g_item_weapons = atoi(val) != 0; else if (strcmp(key, "crates") == 0) g_item_crates = atoi(val) != 0; else if (strcmp(key, "distance") == 0) g_item_distance = atoi(val) != 0; else if (strcmp(key, "max_dist") == 0) { int v = atoi(val); if (v < 0) v = 0; // 0 = unlimited if (v > 10000) v = 10000; g_item_max_dist = v; } } if (in_aim) { if (strcmp(key, "smooth") == 0) { int v = atoi(val); if (v < 1) v = 1; if (v > 45) v = 45; g_aim_smooth = v; } else if (strcmp(key, "bone_head") == 0) { g_aim_bones[AIM_BONE_HEAD] = atoi(val) != 0; } else if (strcmp(key, "bone_torso") == 0) { g_aim_bones[AIM_BONE_TORSO] = atoi(val) != 0; } else if (strcmp(key, "bone_larm") == 0) { g_aim_bones[AIM_BONE_LARM] = atoi(val) != 0; } else if (strcmp(key, "bone_rarm") == 0) { g_aim_bones[AIM_BONE_RARM] = atoi(val) != 0; } else if (strcmp(key, "bone_lleg") == 0) { g_aim_bones[AIM_BONE_LLEG] = atoi(val) != 0; } else if (strcmp(key, "bone_rleg") == 0) { g_aim_bones[AIM_BONE_RLEG] = atoi(val) != 0; } else if (strcmp(key, "vischeck") == 0) { g_aim_vischeck = atoi(val) != 0; } else if (strcmp(key, "fov") == 0) { int v = atoi(val); if (v < 1) v = 1; if (v > 180) v = 180; g_aim_fov = v; } else if (strcmp(key, "fov_circle") == 0) { g_aim_fov_circle = atoi(val) != 0; } else if (strcmp(key, "punch") == 0) { g_aim_punch = atoi(val) != 0; } } if (in_view) { int *dst = nullptr; int lo = 0, hi = 0, stock = 0; if (strcmp(key, "fov") == 0) { dst = &g_fov_camera; lo = 40; hi = 140; stock = 75; } else if (strcmp(key, "vmfov") == 0) { dst = &g_fov_viewmodel; lo = 20; hi = 120; stock = 54; } else if (strcmp(key, "tonemap") == 0) { dst = &g_tonemap; lo = 0; hi = 30; stock = 0; } if (dst) { int v = atoi(val); if (v <= 0) v = stock; // old configs used 0 as "off" if (v < lo) v = lo; if (v > hi) v = hi; *dst = v; } } if (in_movement) { if (strcmp(key, "crouchjump") == 0) g_auto_crouchjump = atoi(val) != 0; else if (strcmp(key, "edgejump") == 0) g_edge_jump = atoi(val) != 0; } if (in_tint) { if (strcmp(key, "world") == 0) g_tint_world_on = atoi(val) != 0; else if (strcmp(key, "sky") == 0) g_tint_sky_on = atoi(val) != 0; else if (strcmp(key, "prop") == 0) g_tint_prop_on = atoi(val) != 0; else if (strcmp(key, "wr") == 0 || strcmp(key, "wg") == 0 || strcmp(key, "wb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_tint_world_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } else if (strcmp(key, "sr") == 0 || strcmp(key, "sg") == 0 || strcmp(key, "sb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_tint_sky_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } else if (strcmp(key, "pr") == 0 || strcmp(key, "pg") == 0 || strcmp(key, "pb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_tint_prop_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } } if (in_fog) { if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || strcmp(key, "b") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_fog_rgb[key[0] == 'r' ? 0 : key[0] == 'g' ? 1 : 2] = v; } else if (strcmp(key, "start") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 8000) v = 8000; g_fog_start = v; } else if (strcmp(key, "end") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 20000) v = 20000; g_fog_end = v; } else if (strcmp(key, "skybox") == 0) { g_fog_skybox = atoi(val) != 0; } else if (strcmp(key, "disable") == 0) { g_fog_disable = atoi(val) != 0; } } if (in_esp) { if (strcmp(key, "neutral") == 0) g_esp_opt[ESP_OPT_SHOW_NEUTRAL] = atoi(val) != 0; else if (strcmp(key, "healthbar") == 0) g_esp_opt[ESP_OPT_SHOW_HEALTHBAR] = atoi(val) != 0; else if (strcmp(key, "healthnum") == 0) g_esp_opt[ESP_OPT_SHOW_HEALTHNUM] = atoi(val) != 0; else if (strcmp(key, "name") == 0) g_esp_opt[ESP_OPT_SHOW_NAME] = atoi(val) != 0; else if (strcmp(key, "skeleton") == 0) g_esp_opt[ESP_OPT_SHOW_SKELETON] = atoi(val) != 0; else if (strcmp(key, "box") == 0) g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; else if (strcmp(key, "speedgraph") == 0) g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH] = atoi(val) != 0; else if (strcmp(key, "dmg") == 0) g_esp_opt[ESP_OPT_DAMAGE_TAGS] = atoi(val) != 0; else if (strcmp(key, "alert") == 0) g_esp_opt[ESP_OPT_ALERT] = atoi(val) != 0; else if (strcmp(key, "weapon") == 0) g_esp_opt[ESP_OPT_WEAPON] = atoi(val) != 0; else if (strcmp(key, "arrows") == 0) g_esp_opt[ESP_OPT_ARROWS] = atoi(val) != 0; else if (strcmp(key, "hr") == 0 || strcmp(key, "hg") == 0 || strcmp(key, "hb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_esp_col_hostile[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } else if (strcmp(key, "nr") == 0 || strcmp(key, "ng") == 0 || strcmp(key, "nb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_esp_col_neutral[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } else if (strcmp(key, "fr") == 0 || strcmp(key, "fg") == 0 || strcmp(key, "fb") == 0) { int v = atoi(val); if (v < 0) v = 0; if (v > 255) v = 255; g_esp_col_friendly[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; } } if (strcmp(key, "active") == 0) cur->active = atoi(val) != 0; else if (strcmp(key, "bind") == 0) cur->bind = atoi(val); } } fclose(f); return true; } void config_reload() { if (config_load()) toastf(TOAST_INFO, "Config reloaded"); else toastf(TOAST_WARN, "Config not found; kept current settings"); } const char *config_path_cstr() { static std::string path; path = config_path(); return path.c_str(); } const char *config_file_cstr(const char *name) { static std::string path; std::string p = config_path(); size_t slash = p.find_last_of('/'); std::string dir = slash == std::string::npos ? std::string(".") : p.substr(0, slash); path = dir + "/" + (name ? name : ""); return path.c_str(); } void config_reset_defaults() { g_bhop = {true, 0}; g_bhop_autostrafe = true; g_abh = {true, 0}; g_jumpbug = {true, 0}; g_jumpbug_assist = false; g_melee = {true, 0}; g_spread = {true, 0}; g_spread_no_spread = true; g_esp = {true, 0}; for (int i = 0; i < ESP_OPT_COUNT; i++) g_esp_opt[i] = true; g_item_esp = {true, 0}; g_fullbright = {false, 0}; g_world_tint = {false, 0}; g_tint_world_on = true; g_tint_sky_on = true; g_tint_prop_on = true; g_tint_world_rgb[0] = g_tint_world_rgb[1] = g_tint_world_rgb[2] = 255; g_tint_sky_rgb[0] = g_tint_sky_rgb[1] = g_tint_sky_rgb[2] = 255; g_tint_prop_rgb[0] = g_tint_prop_rgb[1] = g_tint_prop_rgb[2] = 255; g_fog = {false, 0}; g_fog_skybox = true; g_fog_disable = false; g_fog_rgb[0] = 160; g_fog_rgb[1] = 170; g_fog_rgb[2] = 190; g_fog_start = 200; g_fog_end = 3000; g_skybox = {false, 0}; g_item_health = true; g_item_ammo = true; g_item_armor = true; g_item_weapons = true; g_item_crates = true; g_item_distance = true; g_item_max_dist = 5000; g_aimbot = {true, 604}; g_triggerbot = {true, 603}; g_silentaim = {true, 602}; for (int i = 0; i < AIM_BONE_COUNT; i++) g_aim_bones[i] = false; g_aim_bones[AIM_BONE_HEAD] = true; g_aim_smooth = 5; g_aim_vischeck = true; g_aim_fov = 60; g_aim_fov_circle = true; g_aim_punch = true; g_spread_engine = true; g_auto_crouchjump = false; g_edge_jump = false; g_fov_camera = 75; g_fov_viewmodel = 54; g_tonemap = 0; g_esp_col_hostile[0] = 255; g_esp_col_hostile[1] = 60; g_esp_col_hostile[2] = 60; g_esp_col_neutral[0] = 220; g_esp_col_neutral[1] = 220; g_esp_col_neutral[2] = 220; g_esp_col_friendly[0] = 60; g_esp_col_friendly[1] = 255; g_esp_col_friendly[2] = 60; config_save(); } // simplified scancode-to-name for common keys const char *scancode_name(int sc) { switch (sc) { case 0: return "None"; case 4: return "A"; case 5: return "B"; case 6: return "C"; case 7: return "D"; case 8: return "E"; case 9: return "F"; case 10: return "G"; case 11: return "H"; case 12: return "I"; case 13: return "J"; case 14: return "K"; case 15: return "L"; case 16: return "M"; case 17: return "N"; case 18: return "O"; case 19: return "P"; case 20: return "Q"; case 21: return "R"; case 22: return "S"; case 23: return "T"; case 24: return "U"; case 25: return "V"; case 26: return "W"; case 27: return "X"; case 28: return "Y"; case 29: return "Z"; case 30: return "1"; case 31: return "2"; case 32: return "3"; case 33: return "4"; case 34: return "5"; case 35: return "6"; case 36: return "7"; case 37: return "8"; case 38: return "9"; case 39: return "0"; case 40: return "Enter"; case 41: return "Escape"; case 42: return "Backspace"; case 43: return "Tab"; case 44: return "Space"; case 45: return "Grave"; case 46: return "Minus"; case 47: return "Equals"; case 48: return "LBracket"; case 49: return "RBracket"; case 50: return "Backslash"; case 51: return "Semicolon"; case 52: return "Apostrophe"; case 53: return "Tilde"; case 54: return "Comma"; case 55: return "Period"; case 56: return "Slash"; case 57: return "CapsLock"; case 79: return "Right"; case 80: return "Left"; case 81: return "Down"; case 82: return "Up"; case 58: return "F1"; case 59: return "F2"; case 60: return "F3"; case 61: return "F4"; case 62: return "F5"; case 63: return "F6"; case 64: return "F7"; case 65: return "F8"; case 66: return "F9"; case 67: return "F10"; case 68: return "F11"; case 69: return "F12"; case 73: return "Insert"; case 74: return "Home"; case 75: return "PgUp"; case 76: return "Delete"; case 77: return "End"; case 78: return "PgDn"; case 600: return "MouseL"; case 601: return "MouseR"; case 602: return "MouseM"; case 603: return "Mouse4"; case 604: return "Mouse5"; default: { static char buf[16]; snprintf(buf, sizeof(buf), "Key%d", sc); return buf; } } } // held state for feature binds static bool g_prev_key_state[608] = {}; // read SDL_GetKeyboardState — the game's own SDL is in-process using SDL_GetKeyboardState_fn = const uint8_t *(*)(int *); static SDL_GetKeyboardState_fn SDL_GetKeyboardState_real = nullptr; // mouse buttons map to pseudo-scancodes 600..604 static constexpr int MOUSE_BASE = 600; // 600=L 601=R 602=M 603=X1 604=X2 using SDL_GetMouseState_fn = unsigned (*)(int *, int *); static SDL_GetMouseState_fn SDL_GetMouseState_real = nullptr; static void poll_mouse() { if (!SDL_GetMouseState_real) SDL_GetMouseState_real = (SDL_GetMouseState_fn)dlsym(RTLD_DEFAULT, "SDL_GetMouseState"); if (!SDL_GetMouseState_real) return; int mx, my; unsigned mask = SDL_GetMouseState_real(&mx, &my); for (int b = 0; b < 5; b++) { bool down = (mask & (1u << b)) != 0; // SDL buttons 1..5 = bits 0..4 int sc = MOUSE_BASE + b; g_prev_key_state[sc] = down; } } static void poll_keyboard() { if (!SDL_GetKeyboardState_real) SDL_GetKeyboardState_real = (SDL_GetKeyboardState_fn)dlsym(RTLD_DEFAULT, "SDL_GetKeyboardState"); if (!SDL_GetKeyboardState_real) return; int numkeys = 0; const uint8_t *state = SDL_GetKeyboardState_real(&numkeys); if (state) for (int i = 0; i < numkeys && i < MOUSE_BASE; i++) g_prev_key_state[i] = state[i] != 0; poll_mouse(); } static bool key_held(int sc) { return sc >= 0 && sc < MOUSE_BASE + 5 && g_prev_key_state[sc]; } // feature gate queried from hl2h.cpp do_move(): feature must be Active AND // its bind key held (always-on when unbound). static FeatureState *feature_state(int feature) { static FeatureState *feats[FEAT_COUNT] = { &g_bhop, &g_abh, &g_esp, &g_aimbot, &g_triggerbot, &g_silentaim, &g_jumpbug, &g_melee, &g_spread, &g_item_esp, &g_fullbright, &g_world_tint, &g_fog, &g_skybox}; if (feature < 0 || feature >= FEAT_COUNT) return nullptr; return feats[feature]; } bool feature_active(int feature) { FeatureState *f = feature_state(feature); if (!f || !f->active) return false; return f->bind == 0 || key_held(f->bind); } bool feature_has_bind(int feature) { FeatureState *f = feature_state(feature); return f && f->bind != 0; } bool feature_bind_held(int feature) { FeatureState *f = feature_state(feature); return f && f->bind != 0 && key_held(f->bind); } bool esp_option(int opt) { return opt >= 0 && opt < ESP_OPT_COUNT && g_esp_opt[opt]; } bool bhop_autostrafe_enabled() { return g_bhop_autostrafe; } bool jumpbug_assist_enabled() { return g_jumpbug_assist; } bool spread_no_spread_enabled() { return g_spread_no_spread; } unsigned aimbot_bone_mask() { unsigned m = 0; if (g_aim_bones[AIM_BONE_HEAD]) m |= 1u << 1; // HITGROUP_HEAD if (g_aim_bones[AIM_BONE_TORSO]) m |= (1u << 2) | (1u << 3); // chest + stomach if (g_aim_bones[AIM_BONE_LARM]) m |= 1u << 4; if (g_aim_bones[AIM_BONE_RARM]) m |= 1u << 5; if (g_aim_bones[AIM_BONE_LLEG]) m |= 1u << 6; if (g_aim_bones[AIM_BONE_RLEG]) m |= 1u << 7; return m; } int aimbot_smooth_step() { return g_aim_smooth; } bool aimbot_vischeck_enabled() { return g_aim_vischeck; } int aimbot_fov() { return g_aim_fov; } bool aim_punch_enabled() { return g_aim_punch; } bool spread_engine_enabled() { return g_spread_engine; } bool aimbot_fov_circle() { return g_aim_fov_circle; } // --------------------------------------------------------------------------- // overlay rendering (VGUI ISurface) // --------------------------------------------------------------------------- // VGUI surface interface struct ISurface; using HFont = unsigned long; ISurface *g_surface = nullptr; static HFont g_font = 0; static HFont g_font_small = 0; // VGUI_Surface030 vtable slots (confirmed via binary disassembly of vguimatsurface.so): // 8 = PushMakeCurrent(VPANEL, bool useInsets) — reads args at 0xc/0x10(%ebp) // 9 = PopMakeCurrent(VPANEL) — pops the make-current stack at this+0x264/0x270 // 10 = DrawSetColor(int r, int g, int b, int a) — stores rgba at this+0x24 // 12 = DrawFilledRect(int x0, int y0, int x1, int y1) — alpha check at this+0x27 // 17 = DrawSetTextFont(HFont), 18 = DrawSetTextColor(int r,g,b,a) // 20 = DrawSetTextPos(int x, int y), 22 = DrawPrintText(wchar_t*, len, drawType) // 66 = CreateFont(), 67 = AddGlyphSetToFont(font, name, tall, weight, // blur, scanlines, flags, rangeMin, rangeMax) static constexpr int SURF_GetEmbeddedPanel = 6; static constexpr int SURF_PushMakeCurrent = 8; static constexpr int SURF_PopMakeCurrent = 9; static constexpr int SURF_DrawSetColor = 10; static constexpr int SURF_DrawFilledRect = 12; static constexpr int SURF_DrawSetTextFont = 17; static constexpr int SURF_DrawSetTextColor = 18; static constexpr int SURF_DrawSetTextPos = 20; static constexpr int SURF_DrawPrintText = 22; static constexpr int SURF_CreateFont = 66; static constexpr int SURF_AddGlyphSetToFont = 67; static int surf_get_embedded_panel() { using Fn = int (*)(void *); return vfunc(g_surface, SURF_GetEmbeddedPanel)(g_surface); } static void surf_push_make_current(int panel, bool use_insets) { using Fn = void (*)(void *, int, bool); vfunc(g_surface, SURF_PushMakeCurrent)(g_surface, panel, use_insets); } static void surf_pop_make_current(int panel) { using Fn = void (*)(void *, int); vfunc(g_surface, SURF_PopMakeCurrent)(g_surface, panel); } static void surf_draw_set_color(int r, int g, int b, int a) { using Fn = void (*)(void *, int, int, int, int); vfunc(g_surface, SURF_DrawSetColor)(g_surface, r, g, b, a); } static void surf_draw_filled_rect(int x0, int y0, int x1, int y1) { using Fn = void (*)(void *, int, int, int, int); vfunc(g_surface, SURF_DrawFilledRect)(g_surface, x0, y0, x1, y1); } static void surf_draw_set_text_color(int r, int g, int b, int a) { using Fn = void (*)(void *, int, int, int, int); vfunc(g_surface, SURF_DrawSetTextColor)(g_surface, r, g, b, a); } static void surf_draw_set_text_font(HFont f) { using Fn = void (*)(void *, HFont); vfunc(g_surface, SURF_DrawSetTextFont)(g_surface, f); } static void surf_draw_set_text_pos(int x, int y) { using Fn = void (*)(void *, int, int); vfunc(g_surface, SURF_DrawSetTextPos)(g_surface, x, y); } static void surf_draw_print_text(const wchar_t *text, int count) { using Fn = void (*)(void *, const wchar_t *, int, int); vfunc(g_surface, SURF_DrawPrintText)(g_surface, text, count, 0); // 0 = FONT_DRAW_DEFAULT } static HFont surf_create_font() { using Fn = HFont (*)(void *); return vfunc(g_surface, SURF_CreateFont)(g_surface); } static bool surf_set_font_glyph_set(HFont f, const char *name, int tall, int weight, int blur, int scanlines, int flags, int range_min = 0, int range_max = 0) { using Fn = bool (*)(void *, HFont, const char *, int, int, int, int, int, int, int); return vfunc(g_surface, SURF_AddGlyphSetToFont)(g_surface, f, name, tall, weight, blur, scanlines, flags, range_min, range_max); } // wide string conversion for DrawPrintText static void draw_text(int x, int y, const char *text) { wchar_t wtext[256]; mbstowcs(wtext, text, 255); wtext[255] = 0; int len = (int)wcslen(wtext); surf_draw_set_text_font(g_font); surf_draw_set_text_color(255, 255, 255, 255); surf_draw_set_text_pos(x, y); surf_draw_print_text(wtext, len); } void draw_text_col(int x, int y, const char *text, int r, int g, int b) { wchar_t wtext[256]; mbstowcs(wtext, text, 255); wtext[255] = 0; surf_draw_set_text_font(g_font); surf_draw_set_text_color(r, g, b, 255); surf_draw_set_text_pos(x, y); surf_draw_print_text(wtext, (int)wcslen(wtext)); } void draw_text_small_col(int x, int y, const char *text, int r, int g, int b) { wchar_t wtext[256]; mbstowcs(wtext, text, 255); wtext[255] = 0; surf_draw_set_text_font(g_font_small ? g_font_small : g_font); surf_draw_set_text_color(r, g, b, 255); surf_draw_set_text_pos(x, y); surf_draw_print_text(wtext, (int)wcslen(wtext)); } // --------------------------------------------------------------------------- // mat_fullbright // --------------------------------------------------------------------------- static int g_fullbright_applied = -1; static bool g_fullbright_fail_logged = false; void fullbright_invalidate() { g_fullbright_applied = -1; } void fullbright_update() { bool want = feature_active(FEAT_FULLBRIGHT); if ((int)want == g_fullbright_applied) return; // Avoid touching the engine console before the first map/menu frame. The // engine may still be initializing its command buffer. if (!want && g_fullbright_applied < 0) { g_fullbright_applied = 0; return; } bool ok = cvar_set_int("mat_fullbright", want ? 1 : 0); g_fullbright_applied = want; if (ok) { if (want) logf("fullbright: on"); else logd("fullbright: off"); return; } if (want && !g_fullbright_fail_logged) { g_fullbright_fail_logged = true; logf("fullbright: mat_fullbright cvar unavailable"); toastf(TOAST_ERROR, "Fullbright unavailable: cvar interface missing"); } } // Draw overlay. MUST be called from inside the ISurface paint path // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in // PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. void outer_draw() { poll_keyboard(); fullbright_update(); world_tint_update(); prop_tint_update(); fog_update(); gui_handle_input(); // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; bool esp_on = feature_active(FEAT_ESP); bool items_on = feature_active(FEAT_ITEM_ESP); bool toasts = gui_toasts_active(); if (!g_surface || (!gui_is_open() && !draw_test && !esp_on && !items_on && !toasts)) return; // Level/menu -> game transitions can invalidate the VGUI font handle. // Recreate it when the local player entity changes (menu -> map, map load). static void *last_local = nullptr; bool font_reset = false; if (g_iface.entitylist) { int local = engine_get_local_player(); void *le = local > 0 ? entitylist_get(local) : nullptr; if (le != last_local) { last_local = le; font_reset = true; world_tint_invalidate(); fog_invalidate(); skybox_invalidate(); } } if (font_reset && g_font) { g_font = 0; g_font_small = 0; logd("font: reset (local entity change)"); } bool overlay_font = true; if (!g_font) { static const char *families[] = { "Liberation Sans", "DejaVu Sans", "Noto Sans", "Tahoma", "Verdana", "Arial", nullptr}; bool font_ok = false; const char *chosen = nullptr; for (int fi = 0; families[fi] && !font_ok; fi++) { g_font = surf_create_font(); if (!g_font) continue; if (surf_set_font_glyph_set(g_font, families[fi], 16, 400, 0, 0, 0x400, 0x20, 0xFF)) { logd("outer_draw: font family %s", families[fi]); font_ok = true; chosen = families[fi]; } else { g_font = 0; } } if (font_ok && chosen && !g_font_small) { g_font_small = surf_create_font(); if (g_font_small && !surf_set_font_glyph_set(g_font_small, chosen, 10, 400, 0, 0, 0x400, 0x20, 0xFF)) g_font_small = 0; } if (!font_ok) { logf("outer_draw: no usable font"); toastf(TOAST_ERROR, "No usable overlay font"); overlay_font = false; } } int panel = surf_get_embedded_panel(); if (!panel) return; surf_push_make_current(panel, false); if (esp_on && overlay_font) esp_draw(); if (items_on && overlay_font) esp_draw_items(); if (draw_test && overlay_font) { surf_draw_set_color(255, 0, 0, 255); surf_draw_filled_rect(20, 20, 180, 60); surf_draw_set_color(255, 255, 255, 255); draw_text(28, 36, "HL2H DRAW TEST"); } if (gui_is_open()) gui_draw(); if (toasts) gui_draw_toasts(); surf_pop_make_current(panel); } // --------------------------------------------------------------------------- // ISurface discovery — try known modules and all loaded .so files // --------------------------------------------------------------------------- static ISurface *find_surface_interface() { static const char *known[] = {"engine.so", "client.so", nullptr}; static const char *iface_names[] = { "VGUI_Surface030", "VGUI_Surface001", "MatSystemSurface008", "MatSystemSurface001", "VGUI_Surface002", "VGUI_SURFACE001", nullptr, }; // try known modules for (const char **p = known; *p; p++) { void *h = dlopen(*p, RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); if (!h) continue; auto fn = (CreateInterfaceFn)dlsym(h, "CreateInterface"); if (!fn) continue; for (const char **n = iface_names; *n; n++) { int ret = 0; ISurface *s = (ISurface *)fn(*n, &ret); if (s) { logf("ISurface found: %s via %s (ret=%d)", *n, *p, ret); return s; } } } // 2) scan /proc/self/maps for every loaded .so, try CreateInterface on each FILE *maps = fopen("/proc/self/maps", "r"); if (!maps) return nullptr; char line[1024]; while (fgets(line, sizeof(line), maps)) { char *p = strchr(line, '/'); if (!p) continue; size_t pl = strlen(p); while (pl && (p[pl-1] == '\n' || p[pl-1] == '\r')) p[--pl] = 0; if (!strstr(p, ".so")) continue; void *h = dlopen(p, RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); if (!h) continue; auto fn = (CreateInterfaceFn)dlsym(h, "CreateInterface"); if (!fn) continue; for (const char **n = iface_names; *n; n++) { int ret = 0; ISurface *s = (ISurface *)fn(*n, &ret); if (s) { logf("ISurface found: %s via %s", *n, p); fclose(maps); return s; } } } fclose(maps); return nullptr; } void install_menu() { register_bundled_font(); logf("config: %s", config_path().c_str()); if (!config_load()) toastf(TOAST_INFO, "No config found; using defaults"); gui_config_init(); // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and // by outer_draw). DrawPrintText hook is installed from hkdCreateMove. g_surface = find_surface_interface(); if (!g_surface) { logf("ISurface not found; menu disabled"); toastf(TOAST_ERROR, "Overlay unavailable: ISurface not found"); return; } logf("ISurface = %p", g_surface); }