// studio.cpp — colour static props at the StudioRender level. // // Dynamic props are tinted through their client entity's m_clrRender (see // esp.cpp). Map-static props have no entity: the engine's static-prop manager // calls IStudioRender::DrawModelStaticProp, and the colour modulation used for // the draw comes from IStudioRender::SetColorModulation. Hook both on the // interface object so the prop colour is substituted only while a prop model // is being drawn. // // VStudioRender025 vtable (verified against studiorender.so's // CStudioRenderContext vtable at .data.rel.ro): // 27 = SetColorModulation(float const*) // 28 = SetAlphaModulation(float) // 30 = DrawModelStaticProp(const DrawModelInfo_t&, const matrix3x4_t&, int) // (The 2013 SDK header lists these five slots earlier; this build's layout // has additional methods before them.) #include "hl2h.h" #include #include #include namespace { using SetColorFn = void (*)(void *, const float *); using SetAlphaFn = void (*)(void *, float); using DrawStaticFn = void (*)(void *, void *, void *, int); using DrawModelFn = void (*)(void *, void *, void *, void *, void *, void *, int); using DrawArrayFn = void (*)(void *, void *, int, void *, int, int); SetColorFn g_orig_set_color = nullptr; SetAlphaFn g_orig_set_alpha = nullptr; DrawStaticFn g_orig_draw_static = nullptr; DrawModelFn g_orig_draw_model = nullptr; DrawArrayFn g_orig_draw_array = nullptr; bool g_install_tried = false; bool g_installed = false; int g_prop_depth = 0; int g_static_calls = 0; int g_static_props = 0; bool prop_tint_on() { return feature_active(FEAT_WORLD_TINT) && g_tint_prop_on; } void prop_color(float out[4]) { out[0] = (float)g_tint_prop_rgb[0] / 255.0f; out[1] = (float)g_tint_prop_rgb[1] / 255.0f; out[2] = (float)g_tint_prop_rgb[2] / 255.0f; out[3] = 1.0f; } // DrawModelInfo_t starts with studiohdr_t*; studiohdr_t::name is at +12 // (id, version, checksum, then char name[64]). const char *info_model_name(void *info) { if (!info) return nullptr; void *hdr = *(void **)info; if (!hdr) return nullptr; const char *name = (const char *)hdr + 12; size_t n = strnlen(name, 128); if (n < 7 || n >= 128) return nullptr; for (size_t i = 0; i < n; i++) { unsigned char c = (unsigned char)name[i]; if (c < 0x20 || c > 0x7e) return nullptr; } return name; } bool is_prop_model(const char *name) { return name && strncmp(name, "models/props", 12) == 0; } void hook_set_color(void *self, const float *color) { if (g_prop_depth > 0 && prop_tint_on()) { float c[4]; prop_color(c); g_orig_set_color(self, c); return; } g_orig_set_color(self, color); } void hook_set_alpha(void *self, float alpha) { if (g_prop_depth > 0 && prop_tint_on()) { g_orig_set_alpha(self, 1.0f); return; } g_orig_set_alpha(self, alpha); } void force_prop_color(void *self) { float c[4]; prop_color(c); g_orig_set_color(self, c); g_orig_set_alpha(self, 1.0f); } void hook_draw_model(void *self, void *results, void *info, void *bone, void *flex, void *flex_delayed, int flags) { const char *model = info_model_name(info); bool prop = prop_tint_on() && is_prop_model(model); if (g_static_calls < 3 && prop) logf("studio: DrawModel prop model=\"%s\"", model ? model : "?"); if (prop) { force_prop_color(self); g_prop_depth++; } g_orig_draw_model(self, results, info, bone, flex, flex_delayed, flags); if (prop) g_prop_depth--; } void hook_draw_array(void *self, void *info, int count, void *instances, int stride, int flags) { const char *model = info_model_name(info); bool prop = prop_tint_on() && is_prop_model(model); if (g_static_calls < 3) logf("studio: DrawModelArray count=%d model=\"%s\" prop=%d", count, model ? model : "?", (int)prop); if (prop) { force_prop_color(self); g_prop_depth++; } g_orig_draw_array(self, info, count, instances, stride, flags); if (prop) g_prop_depth--; } void hook_draw_static_prop(void *self, void *info, void *model_to_world, int flags) { const char *model = info_model_name(info); bool prop = prop_tint_on() && is_prop_model(model); g_static_calls++; if (prop) g_static_props++; if (g_static_calls <= 3) logf("studio: static draw model=\"%s\" prop=%d", model ? model : "?", (int)prop); if (g_static_calls == 500) logf("studio: static draws=%d prop_draws=%d", g_static_calls, g_static_props); if (prop) { force_prop_color(self); g_prop_depth++; } g_orig_draw_static(self, info, model_to_world, flags); if (prop) g_prop_depth--; } std::string loaded_module_path(const char *needle) { FILE *maps = fopen("/proc/self/maps", "r"); if (!maps) return {}; char line[1024]; std::string found; 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, needle) || !strstr(p, ".so")) continue; found = p; if (!strstr(p, "(deleted)")) break; } fclose(maps); return found; } } // namespace void studio_props_init() { if (g_install_tried) return; g_install_tried = true; std::string path = loaded_module_path("studiorender"); void *h = nullptr; if (!path.empty()) h = dlopen(path.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); if (!h) h = dlopen("studiorender.so", RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); if (!h) { logf("studio: studiorender.so not loaded"); return; } auto cf = (CreateInterfaceFn)dlsym(h, "CreateInterface"); if (!cf) { logf("studio: studiorender CreateInterface missing"); return; } void *sr = cf("VStudioRender025", nullptr); if (!sr) { logf("studio: VStudioRender025 not exposed"); return; } void **old_vt = *(void ***)sr; if (!old_vt) { logf("studio: interface has no vtable"); return; } static void *vt[49]; memcpy(vt, old_vt, sizeof(vt)); g_orig_set_color = (SetColorFn)vt[27]; g_orig_set_alpha = (SetAlphaFn)vt[28]; g_orig_draw_model = (DrawModelFn)vt[29]; g_orig_draw_static = (DrawStaticFn)vt[30]; g_orig_draw_array = (DrawArrayFn)vt[46]; vt[27] = (void *)&hook_set_color; vt[28] = (void *)&hook_set_alpha; vt[29] = (void *)&hook_draw_model; vt[30] = (void *)&hook_draw_static_prop; vt[46] = (void *)&hook_draw_array; *(void ***)sr = vt; g_installed = true; logf("studio: IStudioRender %p hooked (color=%p draw=%p static=%p " "array=%p)", sr, (void *)g_orig_set_color, (void *)g_orig_draw_model, (void *)g_orig_draw_static, (void *)g_orig_draw_array); }