aboutsummaryrefslogtreecommitdiff
path: root/src/portalcolours.c
blob: 89d1d170724f47ed433c8808713bcd255fb732c7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
 * Copyright © 2025 Michael Smith <mikesmiffy128@gmail.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <string.h>

#include "con_.h"
#include "engineapi.h"
#include "errmsg.h"
#include "feature.h"
#include "gametype.h"
#include "hexcolour.h"
#include "hook.h"
#include "intdefs.h"
#include "langext.h"
#include "mem.h"
#include "os.h"
#include "ppmagic.h"
#include "sst.h"
#include "vcall.h"

FEATURE("portal gun colour customisation")
GAMESPECIFIC(Portal1)
REQUIRE_GLOBAL(clientlib)

// It's like the thing Portal Tools does, but at runtime!

DEF_FEAT_CVAR(sst_portal_colour0, "Crosshair colour for gravity beam (RGB hex)",
		"F2CAA7", CON_ARCHIVE)
DEF_FEAT_CVAR(sst_portal_colour1, "Crosshair colour for left portal (RGB hex)",
		"40A0FF", CON_ARCHIVE)
DEF_FEAT_CVAR(sst_portal_colour2, "Crosshair colour for right portal (RGB hex)",
		"FFA020", CON_ARCHIVE)
static struct rgba colours[3] = {
		{242, 202, 167, 255}, {64, 160, 255, 255}, {255, 160, 32, 255}};

static void colourcb(struct con_var *v) {
	// this is stupid and ugly and has no friends, too bad!
	if (v == sst_portal_colour0) {
		hexcolour_rgb(colours[0].bytes, con_getvarstr(v));
	}
	else if (v == sst_portal_colour1) {
		hexcolour_rgb(colours[1].bytes, con_getvarstr(v));
	}
	else /* sst_portal_colour2 */ {
		hexcolour_rgb(colours[2].bytes, con_getvarstr(v));
	}
}

// Original sig is the following but we wanna avoid calling convention weirdness
//typedef struct rgba (*UTIL_Portal_Color_func)(int);
typedef void (*UTIL_Portal_Color_func)(struct rgba *out, int portal);
static UTIL_Portal_Color_func orig_UTIL_Portal_Color;
static void hook_UTIL_Portal_Color(struct rgba *out, int portal) {
	if (portal < 0 || portal > 2) *out = (struct rgba){255, 255, 255, 255};
	else *out = colours[portal];
}

// TODO(compat): would like to do the usual pointer-chasing business instead of
// using hardcoded offsets, but that's pretty hard here. Would probably have to
// do the entprops stuff for ClientClass, get at the portalgun factory, get a
// vtable, find ViewModelDrawn or something, chase through another 4 or 5 call
// offsets to find something that calls UTIL_Portal_Color... that or dig through
// vgui/hud entries, find the crosshair drawing...
//
// For now we do this!

static bool find_UTIL_Portal_Color(void *base) {
	static const uchar x[] = HEXBYTES(8B, 44, 24, 08, 83, E8, 00, 74, 37, 83,
			E8, 01, B1, FF, 74, 1E, 83, E8, 01, 8B, 44, 24, 04, 88);
	// 5135
	orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)mem_offset(base, 0x1BF090);
	if (!memcmp((void *)orig_UTIL_Portal_Color, x, sizeof(x))) return true;
	// 3420
	orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)mem_offset(base, 0x1AA810);
	if (!memcmp((void *)orig_UTIL_Portal_Color, x, sizeof(x))) return true;
	// SteamPipe (7197370) - almost sure to break in a later update!
	static const uchar y[] = HEXBYTES(55, 8B, EC, 8B, 45, 0C, 83, E8, 00, 74,
			24, 48, 74, 16, 48, 8B, 45, 08, 74, 08, C7, 00, FF, FF);
	orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)mem_offset(base, 0x234C00);
	if (!memcmp((void *)orig_UTIL_Portal_Color, y, sizeof(y))) return true;
	return false;
}

INIT {
#ifdef _WIN32
	if_cold (!find_UTIL_Portal_Color(clientlib)) {
		errmsg_errorx("couldn't find UTIL_Portal_Color");
		return FEAT_INCOMPAT;
	}
	orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)hook_inline(
			(void *)orig_UTIL_Portal_Color, (void *)&hook_UTIL_Portal_Color);
	if_cold (!orig_UTIL_Portal_Color) {
		errmsg_errorsys("couldn't hook UTIL_Portal_Color");
		return FEAT_INCOMPAT;
	}
	sst_portal_colour0->cb = &colourcb;
	sst_portal_colour1->cb = &colourcb;
	sst_portal_colour2->cb = &colourcb;
	return FEAT_OK;
#else
#warning TODO(linux): yet more stuff!
	return FEAT_INCOMPAT;
#endif
}

END {
	if_hot (!sst_userunloaded) return;
	unhook_inline((void *)orig_UTIL_Portal_Color);
}

// vi: sw=4 ts=4 noet tw=80 cc=80