diff options
| author | salmonik <salmonik@tuta.io> | 2026-09-12 19:04:22 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-09-12 19:04:22 +0200 |
| commit | 210f3a12ee1d56750d593b4c404a0683f810aa3e (patch) | |
| tree | 926a136c0366616077b03c2e48ffba5af7312d08 /src/menu.cpp | |
| parent | init (diff) | |
| download | hl2h-210f3a12ee1d56750d593b4c404a0683f810aa3e.tar.gz hl2h-210f3a12ee1d56750d593b4c404a0683f810aa3e.zip | |
ISurface overlay working: PTEx draw path, -Werror, menu states, bind-driven features
Diffstat (limited to '')
| -rw-r--r-- | src/menu.cpp | 312 |
1 files changed, 197 insertions, 115 deletions
diff --git a/src/menu.cpp b/src/menu.cpp index 91a0948..c62db47 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | // menu.cpp — menu state, rendering, input, config, paint hook, X11 mouse | 1 | // menu.cpp — menu state, rendering, input, config, paint hook, X11 mouse |
| 2 | #include "hl2h.h" | 2 | #include "hl2h.h" |
| 3 | #include <cstdarg> | 3 | #include <cmath> |
| 4 | #include <cstdio> | 4 | #include <cstdio> |
| 5 | #include <cstdlib> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | 7 | ||
| 7 | // --------------------------------------------------------------------------- | 8 | // --------------------------------------------------------------------------- |
| @@ -104,14 +105,14 @@ static FeatureState g_bhop{true, 0}; | |||
| 104 | static FeatureState g_abh{true, 0}; | 105 | static FeatureState g_abh{true, 0}; |
| 105 | 106 | ||
| 106 | // tree | 107 | // tree |
| 107 | static MenuNode g_root{NODE_ROOT, "HL2hake", true, 0, {}}; | 108 | static MenuNode g_root{NODE_ROOT, "HL2hake", true, 0, {}, nullptr, nullptr, nullptr}; |
| 108 | static MenuNode g_climb_feat{NODE_FEATURE, "Climb", true, 2, {}}; | 109 | static MenuNode g_climb_feat{NODE_FEATURE, "Climb", true, 2, {}, nullptr, nullptr, nullptr}; |
| 109 | static MenuNode g_climb_active{NODE_TOGGLE, "Active", false, 0, {}, &g_climb.active}; | 110 | static MenuNode g_climb_active{NODE_TOGGLE, "Active", false, 0, {}, &g_climb.active, nullptr, nullptr}; |
| 110 | static MenuNode g_climb_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_climb.bind, "None"}; | 111 | static MenuNode g_climb_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_climb.bind, "None"}; |
| 111 | static MenuNode g_bhop_feat{NODE_FEATURE, "Bunnyhop", true, 1, {}}; | 112 | static MenuNode g_bhop_feat{NODE_FEATURE, "Bunnyhop", true, 1, {}, nullptr, nullptr, nullptr}; |
| 112 | static MenuNode g_bhop_active{NODE_TOGGLE, "Active", false, 0, {}, &g_bhop.active}; | 113 | static MenuNode g_bhop_active{NODE_TOGGLE, "Active", false, 0, {}, &g_bhop.active, nullptr, nullptr}; |
| 113 | static MenuNode g_abh_feat{NODE_FEATURE, "ABH", true, 1, {}}; | 114 | static MenuNode g_abh_feat{NODE_FEATURE, "ABH", true, 1, {}, nullptr, nullptr, nullptr}; |
| 114 | static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active}; | 115 | static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; |
| 115 | 116 | ||
| 116 | static MenuNode *g_visible[16]; // flattened visible nodes for arrow navigation | 117 | static MenuNode *g_visible[16]; // flattened visible nodes for arrow navigation |
| 117 | static int g_visible_count = 0; | 118 | static int g_visible_count = 0; |
| @@ -132,6 +133,8 @@ static void build_tree() { | |||
| 132 | g_root.child_count = 3; | 133 | g_root.child_count = 3; |
| 133 | } | 134 | } |
| 134 | 135 | ||
| 136 | static int g_depth[16]; // depth of each visible row (for indentation) | ||
| 137 | |||
| 135 | static void flatten(MenuNode *node, int depth) { | 138 | static void flatten(MenuNode *node, int depth) { |
| 136 | if (g_visible_count >= 16) | 139 | if (g_visible_count >= 16) |
| 137 | return; | 140 | return; |
| @@ -139,6 +142,7 @@ static void flatten(MenuNode *node, int depth) { | |||
| 139 | : (node->child_count > 0) ? NODE_FEATURE | 142 | : (node->child_count > 0) ? NODE_FEATURE |
| 140 | : (node->active_ptr) ? NODE_TOGGLE | 143 | : (node->active_ptr) ? NODE_TOGGLE |
| 141 | : NODE_BIND; | 144 | : NODE_BIND; |
| 145 | g_depth[g_visible_count] = depth; | ||
| 142 | g_visible[g_visible_count++] = node; | 146 | g_visible[g_visible_count++] = node; |
| 143 | if (node->expanded) { | 147 | if (node->expanded) { |
| 144 | for (int i = 0; i < node->child_count; i++) | 148 | for (int i = 0; i < node->child_count; i++) |
| @@ -297,11 +301,28 @@ static bool key_pressed(int sc) { | |||
| 297 | return sc >= 0 && sc < 512 && g_prev_key_state[sc]; | 301 | return sc >= 0 && sc < 512 && g_prev_key_state[sc]; |
| 298 | } | 302 | } |
| 299 | 303 | ||
| 304 | // feature gate queried from hl2h.cpp do_move(): feature must be Active AND | ||
| 305 | // the activation condition met. Climb activates on its bind key held (falls | ||
| 306 | // back to the historic middle-mouse behavior when unbound); other features | ||
| 307 | // activate on their bind key held, or are always-on when unbound. | ||
| 308 | bool feature_active(int feature) { | ||
| 309 | static FeatureState *feats[3] = {&g_climb, &g_bhop, &g_abh}; | ||
| 310 | if (feature < 0 || feature > 2) | ||
| 311 | return false; | ||
| 312 | FeatureState *f = feats[feature]; | ||
| 313 | if (!f->active) | ||
| 314 | return false; | ||
| 315 | if (f->bind == 0) | ||
| 316 | return feature == FEAT_CLIMB ? middle_mouse_down() : true; | ||
| 317 | return key_held(f->bind); | ||
| 318 | } | ||
| 319 | |||
| 300 | static void menu_handle_input() { | 320 | static void menu_handle_input() { |
| 301 | poll_keyboard(); | 321 | poll_keyboard(); |
| 302 | 322 | ||
| 303 | if (!g_menu_open) { | 323 | if (!g_menu_open) { |
| 304 | if (key_pressed(SCAN_INSERT)) { | 324 | if (key_pressed(SCAN_INSERT)) { |
| 325 | std::logf("insert pressed"); | ||
| 305 | g_menu_open = true; | 326 | g_menu_open = true; |
| 306 | g_bind_mode = false; | 327 | g_bind_mode = false; |
| 307 | rebuild_visible(); | 328 | rebuild_visible(); |
| @@ -384,29 +405,53 @@ static void menu_handle_input() { | |||
| 384 | // --------------------------------------------------------------------------- | 405 | // --------------------------------------------------------------------------- |
| 385 | 406 | ||
| 386 | // VGUI surface interface | 407 | // VGUI surface interface |
| 387 | using CreateInterfaceFn = void *(*)(const char *, int *); | ||
| 388 | struct ISurface; | 408 | struct ISurface; |
| 389 | |||
| 390 | // VGUI font handle (opaque) | ||
| 391 | using HFont = unsigned long; | 409 | using HFont = unsigned long; |
| 392 | 410 | ||
| 393 | // function pointers resolved at init | 411 | ISurface *g_surface = nullptr; |
| 394 | static ISurface *g_surface = nullptr; | ||
| 395 | static HFont g_font = 0; | 412 | static HFont g_font = 0; |
| 396 | 413 | ||
| 397 | // surface vtable indices for 2007-era VGUI_SURFACE001 | 414 | // VGUI_Surface030 vtable slots (confirmed via binary disassembly of vguimatsurface.so): |
| 398 | // verified: slot 16 = DrawSetTextColor, slot 17 = DrawSetTextFont, | 415 | // 8 = PushMakeCurrent(VPANEL, bool useInsets) — reads args at 0xc/0x10(%ebp) |
| 399 | // slot 19 = DrawSetTextPos, slot 22 = DrawPrintText, | 416 | // 9 = PopMakeCurrent(VPANEL) — pops the make-current stack at this+0x264/0x270 |
| 400 | // slot 23 = DrawGetTextSize, slot 31 = CreateNewFont, slot 32 = SetFontGlyphSet | 417 | // 10 = DrawSetColor(int r, int g, int b, int a) — stores rgba at this+0x24 |
| 401 | // These are from the standard VGUI surface vtable layout; will be verified | 418 | // 12 = DrawFilledRect(int x0, int y0, int x1, int y1) — alpha check at this+0x27 |
| 402 | // against runtime if they fail. | 419 | // 17 = DrawSetTextFont(HFont), 18 = DrawSetTextColor(int r,g,b,a) |
| 403 | static constexpr int SURF_DrawSetTextColor = 16; | 420 | // 20 = DrawSetTextPos(int x, int y), 22 = DrawPrintText(wchar_t*, len, drawType) |
| 421 | // 66 = CreateFont(), 67 = AddGlyphSetToFont(font, name, tall, weight, | ||
| 422 | // blur, scanlines, flags, rangeMin, rangeMax) | ||
| 423 | static constexpr int SURF_GetEmbeddedPanel = 6; | ||
| 424 | static constexpr int SURF_PushMakeCurrent = 8; | ||
| 425 | static constexpr int SURF_PopMakeCurrent = 9; | ||
| 426 | static constexpr int SURF_DrawSetColor = 10; | ||
| 427 | static constexpr int SURF_DrawFilledRect = 12; | ||
| 404 | static constexpr int SURF_DrawSetTextFont = 17; | 428 | static constexpr int SURF_DrawSetTextFont = 17; |
| 405 | static constexpr int SURF_DrawSetTextPos = 19; | 429 | static constexpr int SURF_DrawSetTextColor = 18; |
| 430 | static constexpr int SURF_DrawSetTextPos = 20; | ||
| 406 | static constexpr int SURF_DrawPrintText = 22; | 431 | static constexpr int SURF_DrawPrintText = 22; |
| 407 | static constexpr int SURF_CreateNewFont = 31; | 432 | static constexpr int SURF_CreateFont = 66; |
| 408 | static constexpr int SURF_SetFontGlyphSet = 32; | 433 | static constexpr int SURF_AddGlyphSetToFont = 67; |
| 409 | 434 | ||
| 435 | static int surf_get_embedded_panel() { | ||
| 436 | using Fn = int (*)(void *); | ||
| 437 | return vfunc<Fn>(g_surface, SURF_GetEmbeddedPanel)(g_surface); | ||
| 438 | } | ||
| 439 | static void surf_push_make_current(int panel, bool use_insets) { | ||
| 440 | using Fn = void (*)(void *, int, bool); | ||
| 441 | vfunc<Fn>(g_surface, SURF_PushMakeCurrent)(g_surface, panel, use_insets); | ||
| 442 | } | ||
| 443 | static void surf_pop_make_current(int panel) { | ||
| 444 | using Fn = void (*)(void *, int); | ||
| 445 | vfunc<Fn>(g_surface, SURF_PopMakeCurrent)(g_surface, panel); | ||
| 446 | } | ||
| 447 | static void surf_draw_set_color(int r, int g, int b, int a) { | ||
| 448 | using Fn = void (*)(void *, int, int, int, int); | ||
| 449 | vfunc<Fn>(g_surface, SURF_DrawSetColor)(g_surface, r, g, b, a); | ||
| 450 | } | ||
| 451 | static void surf_draw_filled_rect(int x0, int y0, int x1, int y1) { | ||
| 452 | using Fn = void (*)(void *, int, int, int, int); | ||
| 453 | vfunc<Fn>(g_surface, SURF_DrawFilledRect)(g_surface, x0, y0, x1, y1); | ||
| 454 | } | ||
| 410 | static void surf_draw_set_text_color(int r, int g, int b, int a) { | 455 | static void surf_draw_set_text_color(int r, int g, int b, int a) { |
| 411 | using Fn = void (*)(void *, int, int, int, int); | 456 | using Fn = void (*)(void *, int, int, int, int); |
| 412 | vfunc<Fn>(g_surface, SURF_DrawSetTextColor)(g_surface, r, g, b, a); | 457 | vfunc<Fn>(g_surface, SURF_DrawSetTextColor)(g_surface, r, g, b, a); |
| @@ -420,20 +465,23 @@ static void surf_draw_set_text_pos(int x, int y) { | |||
| 420 | vfunc<Fn>(g_surface, SURF_DrawSetTextPos)(g_surface, x, y); | 465 | vfunc<Fn>(g_surface, SURF_DrawSetTextPos)(g_surface, x, y); |
| 421 | } | 466 | } |
| 422 | static void surf_draw_print_text(const wchar_t *text, int count) { | 467 | static void surf_draw_print_text(const wchar_t *text, int count) { |
| 423 | using Fn = void (*)(void *, const wchar_t *, int); | 468 | using Fn = void (*)(void *, const wchar_t *, int, int); |
| 424 | vfunc<Fn>(g_surface, SURF_DrawPrintText)(g_surface, text, count); | 469 | vfunc<Fn>(g_surface, SURF_DrawPrintText)(g_surface, text, count, 0); // 0 = FONT_DRAW_DEFAULT |
| 425 | } | 470 | } |
| 426 | static HFont surf_create_font() { | 471 | static HFont surf_create_font() { |
| 427 | using Fn = HFont (*)(void *); | 472 | using Fn = HFont (*)(void *); |
| 428 | return vfunc<Fn>(g_surface, SURF_CreateNewFont)(g_surface); | 473 | return vfunc<Fn>(g_surface, SURF_CreateFont)(g_surface); |
| 429 | } | 474 | } |
| 430 | static void surf_set_font_glyph_set(HFont f, const char *name, int tall, | 475 | static bool surf_set_font_glyph_set(HFont f, const char *name, int tall, |
| 431 | int weight, int blur, int scanlines, | 476 | int weight, int blur, int scanlines, |
| 432 | int flags) { | 477 | int flags, int range_min = 0, |
| 433 | using Fn = void (*)(void *, HFont, const char *, int, int, int, int, int, int, | 478 | int range_max = 0) { |
| 479 | using Fn = bool (*)(void *, HFont, const char *, int, int, int, int, int, int, | ||
| 434 | int); | 480 | int); |
| 435 | vfunc<Fn>(g_surface, SURF_SetFontGlyphSet)(g_surface, f, name, tall, weight, | 481 | return vfunc<Fn>(g_surface, SURF_AddGlyphSetToFont)(g_surface, f, name, tall, |
| 436 | blur, scanlines, flags, 0, 0); | 482 | weight, blur, scanlines, |
| 483 | flags, range_min, | ||
| 484 | range_max); | ||
| 437 | } | 485 | } |
| 438 | 486 | ||
| 439 | // wide string conversion for DrawPrintText | 487 | // wide string conversion for DrawPrintText |
| @@ -442,114 +490,148 @@ static void draw_text(int x, int y, const char *text) { | |||
| 442 | mbstowcs(wtext, text, 255); | 490 | mbstowcs(wtext, text, 255); |
| 443 | wtext[255] = 0; | 491 | wtext[255] = 0; |
| 444 | int len = (int)wcslen(wtext); | 492 | int len = (int)wcslen(wtext); |
| 493 | surf_draw_set_text_font(g_font); | ||
| 494 | surf_draw_set_text_color(255, 255, 255, 255); | ||
| 445 | surf_draw_set_text_pos(x, y); | 495 | surf_draw_set_text_pos(x, y); |
| 446 | surf_draw_print_text(wtext, len); | 496 | surf_draw_print_text(wtext, len); |
| 447 | } | 497 | } |
| 448 | 498 | ||
| 449 | // --------------------------------------------------------------------------- | 499 | // Draw overlay. MUST be called from inside the ISurface paint path |
| 450 | // paint hook — draws menu overlay every frame | 500 | // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in |
| 451 | // --------------------------------------------------------------------------- | 501 | // PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. |
| 452 | 502 | void outer_draw() { | |
| 453 | using PaintFn = void (*)(void *, int); | 503 | menu_handle_input(); |
| 454 | static PaintFn oPaint = nullptr; | 504 | // HL2H_DRAW_TEST=1 draws a probe rect without needing the menu open |
| 455 | 505 | static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; | |
| 456 | static void hkdPaint(void *self, int mode) { | 506 | if (!g_surface || (!g_menu_open && !draw_test)) |
| 457 | oPaint(self, mode); | 507 | return; |
| 458 | 508 | if (!g_font) { | |
| 459 | // init font on first paint | ||
| 460 | static bool font_inited = false; | ||
| 461 | if (!font_inited) { | ||
| 462 | g_font = surf_create_font(); | 509 | g_font = surf_create_font(); |
| 463 | if (g_font) { | 510 | if (!g_font) { |
| 464 | surf_set_font_glyph_set(g_font, "Consolas", 14, 400, 0, 0, 0); | 511 | logf("outer_draw: CreateFont failed"); |
| 512 | return; | ||
| 513 | } | ||
| 514 | // FONTFLAG_CUSTOM only; Liberation Sans is an installed real TTF (Verdana | ||
| 515 | // substitutes to Noto). Raw glyphs, ASCII range pinned. | ||
| 516 | if (!surf_set_font_glyph_set(g_font, "Liberation Sans", 16, 400, 0, 0, | ||
| 517 | 0x400, 0x20, 0xFF)) { | ||
| 518 | logf("outer_draw: SetFontGlyphSet failed"); | ||
| 519 | g_font = 0; | ||
| 520 | return; | ||
| 465 | } | 521 | } |
| 466 | font_inited = true; | ||
| 467 | } | 522 | } |
| 468 | if (!g_font) | 523 | int panel = surf_get_embedded_panel(); |
| 524 | if (!panel) | ||
| 469 | return; | 525 | return; |
| 470 | 526 | surf_push_make_current(panel, false); | |
| 471 | menu_handle_input(); | 527 | if (draw_test) { |
| 472 | 528 | surf_draw_set_color(255, 0, 0, 255); | |
| 473 | if (!g_menu_open) | 529 | surf_draw_filled_rect(20, 20, 180, 60); |
| 530 | surf_draw_set_color(255, 255, 255, 255); | ||
| 531 | draw_text(28, 36, "HL2H DRAW TEST"); | ||
| 532 | } | ||
| 533 | if (!g_menu_open) { | ||
| 534 | surf_pop_make_current(panel); | ||
| 474 | return; | 535 | return; |
| 475 | 536 | } | |
| 476 | surf_draw_set_text_font(g_font); | ||
| 477 | |||
| 478 | int y = 10; | ||
| 479 | rebuild_visible(); | ||
| 480 | for (int i = 0; i < g_visible_count; i++) { | 537 | for (int i = 0; i < g_visible_count; i++) { |
| 481 | MenuNode *n = g_visible[i]; | 538 | MenuNode *n = g_visible[i]; |
| 482 | int indent = 0; | 539 | char line[96]; |
| 483 | 540 | int x = 12 + g_depth[i] * 12; | |
| 484 | // count depth by counting how many parents are visible before this node | 541 | snprintf(line, sizeof(line), "%s %s", |
| 485 | // (simple: first node = depth 0, others = depth 1 if label is Active/Bind) | 542 | (i == g_selected) ? ">" : (n->child_count ? "-" : " "), n->label); |
| 486 | if (n->type == NODE_TOGGLE || n->type == NODE_BIND) | 543 | draw_text(x, 10 + i * 20, line); |
| 487 | indent = 20; | 544 | // right-aligned state |
| 488 | |||
| 489 | char buf[256]; | ||
| 490 | if (n->type == NODE_TOGGLE) { | 545 | if (n->type == NODE_TOGGLE) { |
| 491 | snprintf(buf, sizeof(buf), "%s: %s", n->label, | 546 | draw_text(200, 10 + i * 20, *n->active_ptr ? "[on]" : "[off]"); |
| 492 | *n->active_ptr ? "ON" : "OFF"); | ||
| 493 | } else if (n->type == NODE_BIND) { | 547 | } else if (n->type == NODE_BIND) { |
| 494 | snprintf(buf, sizeof(buf), "%s: %s", n->label, | 548 | if (g_bind_mode && i == g_selected) |
| 495 | scancode_name(*n->bind_ptr)); | 549 | draw_text(200, 10 + i * 20, "[press key]"); |
| 496 | if (g_bind_mode && g_selected == i) | 550 | else { |
| 497 | snprintf(buf, sizeof(buf), "%s: ...", n->label); | 551 | /* stable name for current bind */ |
| 498 | } else { | 552 | static char bn[3][32]; |
| 499 | snprintf(buf, sizeof(buf), "%s", n->label); | 553 | static FeatureState *feats[] = {&g_climb, &g_bhop, &g_abh}; |
| 500 | } | 554 | int fi = 0; |
| 501 | 555 | for (int k = 0; k < 3; k++) | |
| 502 | if (i == g_selected) { | 556 | if (n->bind_ptr == &feats[k]->bind) |
| 503 | surf_draw_set_text_color(255, 255, 0, 255); // yellow highlight | 557 | fi = k; |
| 504 | } else if (n->type == NODE_TOGGLE) { | 558 | snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); |
| 505 | surf_draw_set_text_color(*n->active_ptr ? 0 : 255, 255, 0, 255); | 559 | draw_text(200, 10 + i * 20, bn[fi]); |
| 506 | } else { | 560 | } |
| 507 | surf_draw_set_text_color(255, 255, 255, 255); | ||
| 508 | } | 561 | } |
| 509 | |||
| 510 | draw_text(10 + indent, y, buf); | ||
| 511 | y += 18; | ||
| 512 | } | 562 | } |
| 563 | surf_pop_make_current(panel); | ||
| 564 | } | ||
| 513 | 565 | ||
| 514 | // reset text color | 566 | // Call an arbitrary ISurface vtable slot with up to 2 long args (cdecl thiscall |
| 515 | surf_draw_set_text_color(255, 255, 255, 255); | 567 | // on i386: this first, rest on stack). |
| 568 | long surf_call_slot(void *surf, int slot, long a1, long a2) { | ||
| 569 | void **vt = *(void ***)surf; | ||
| 570 | using Fn = long (*)(void *, long, long); | ||
| 571 | return ((Fn)vt[slot])(surf, a1, a2); | ||
| 516 | } | 572 | } |
| 517 | 573 | ||
| 518 | // --------------------------------------------------------------------------- | 574 | // --------------------------------------------------------------------------- |
| 519 | // paint hook installation (called from setup_thread) | 575 | // ISurface discovery — try known modules and all loaded .so files |
| 520 | // --------------------------------------------------------------------------- | 576 | // --------------------------------------------------------------------------- |
| 521 | 577 | ||
| 522 | // VEngineVGui001 vtable indices for this build | 578 | static ISurface *find_surface_interface() { |
| 523 | // (Paint is typically slot 6 — will be verified at runtime; if wrong, | 579 | static const char *known[] = {"engine.so", "client.so", nullptr}; |
| 524 | // we crash and can adjust. Common Source 2007 layout.) | 580 | static const char *iface_names[] = { |
| 525 | static constexpr int VGUI_Paint_SLOT = 6; | 581 | "VGUI_Surface030", "VGUI_Surface001", |
| 526 | 582 | "MatSystemSurface008", "MatSystemSurface001", | |
| 527 | static void install_paint_hook() { | 583 | "VGUI_Surface002", "VGUI_SURFACE001", |
| 528 | // get IEngineVGui from engine | 584 | nullptr, |
| 529 | void *eh = dlopen(g_engine.path.c_str(), | 585 | }; |
| 530 | RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); | 586 | // try known modules |
| 531 | if (!eh) | 587 | for (const char **p = known; *p; p++) { |
| 532 | return; | 588 | void *h = dlopen(*p, RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); |
| 533 | auto ef = (CreateInterfaceFn)dlsym(eh, "CreateInterface"); | 589 | if (!h) continue; |
| 534 | if (!ef) | 590 | auto fn = (CreateInterfaceFn)dlsym(h, "CreateInterface"); |
| 535 | return; | 591 | if (!fn) continue; |
| 536 | void *evgui = ef("VEngineVGui001", nullptr); | 592 | for (const char **n = iface_names; *n; n++) { |
| 537 | if (!evgui) { | 593 | int ret = 0; |
| 538 | logf("VEngineVGui001 not found"); | 594 | ISurface *s = (ISurface *)fn(*n, &ret); |
| 539 | return; | 595 | if (s) { logf("ISurface found: %s via %s (ret=%d)", *n, *p, ret); return s; } |
| 596 | } | ||
| 540 | } | 597 | } |
| 541 | 598 | // 2) scan /proc/self/maps for every loaded .so, try CreateInterface on each | |
| 542 | // hook Paint | 599 | FILE *maps = fopen("/proc/self/maps", "r"); |
| 543 | void **vt = VTABLE(evgui); | 600 | if (!maps) return nullptr; |
| 544 | oPaint = (PaintFn)vt[VGUI_Paint_SLOT]; | 601 | char line[1024]; |
| 545 | vt[VGUI_Paint_SLOT] = (void *)&hkdPaint; | 602 | while (fgets(line, sizeof(line), maps)) { |
| 546 | logf("paint hook installed: Paint %p -> %p", (void *)oPaint, | 603 | char *p = strchr(line, '/'); |
| 547 | (void *)&hkdPaint); | 604 | if (!p) continue; |
| 605 | size_t pl = strlen(p); | ||
| 606 | while (pl && (p[pl-1] == '\n' || p[pl-1] == '\r')) p[--pl] = 0; | ||
| 607 | if (!strstr(p, ".so")) continue; | ||
| 608 | void *h = dlopen(p, RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); | ||
| 609 | if (!h) continue; | ||
| 610 | auto fn = (CreateInterfaceFn)dlsym(h, "CreateInterface"); | ||
| 611 | if (!fn) continue; | ||
| 612 | for (const char **n = iface_names; *n; n++) { | ||
| 613 | int ret = 0; | ||
| 614 | ISurface *s = (ISurface *)fn(*n, &ret); | ||
| 615 | if (s) { | ||
| 616 | logf("ISurface found: %s via %s", *n, p); | ||
| 617 | fclose(maps); | ||
| 618 | return s; | ||
| 619 | } | ||
| 620 | } | ||
| 621 | } | ||
| 622 | fclose(maps); | ||
| 623 | return nullptr; | ||
| 548 | } | 624 | } |
| 549 | 625 | ||
| 550 | // public init function called from setup_thread | ||
| 551 | void install_menu() { | 626 | void install_menu() { |
| 552 | build_tree(); | 627 | build_tree(); |
| 553 | config_load(); | 628 | config_load(); |
| 554 | install_paint_hook(); | 629 | // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and |
| 630 | // by outer_draw). DrawPrintText hook is installed from hkdCreateMove. | ||
| 631 | g_surface = find_surface_interface(); | ||
| 632 | if (!g_surface) { | ||
| 633 | logf("ISurface not found; menu disabled"); | ||
| 634 | return; | ||
| 635 | } | ||
| 636 | logf("ISurface = %p", g_surface); | ||
| 555 | } | 637 | } |
