-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBool.cpp
More file actions
114 lines (99 loc) · 4.06 KB
/
Bool.cpp
File metadata and controls
114 lines (99 loc) · 4.06 KB
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
// This file is part of ClassicAPI.
//
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
#include "Game.h"
#include "Offsets.h"
#include <cstdint>
namespace CVar::Bool {
namespace {
using FindCVar_t = const uint8_t *(__fastcall *)(const char *name);
// Case-insensitive "true" check for the string-form match. Bounded
// 4-char comparison + null terminator — same shape `BookTypeIsPet`
// uses in spell/Info.cpp.
bool IsTrueString(const char *s) {
if (s == nullptr)
return false;
auto lc = [](char c) -> char {
return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c;
};
return lc(s[0]) == 't' && lc(s[1]) == 'r' && lc(s[2]) == 'u' &&
lc(s[3]) == 'e' && s[4] == '\0';
}
// Coerces a cvar's string value to bool per modern semantics:
// - empty / nil → false
// - "0" → false
// - "1" or any numeric non-zero → true
// - "true" (case-insensitive) → true
// - anything else (non-numeric, non-"true") → false
bool StringToBool(const char *s) {
if (s == nullptr || s[0] == '\0')
return false;
if (IsTrueString(s))
return true;
// Parse leading integer; non-zero means true. atoi-style.
bool negative = false;
int idx = 0;
if (s[idx] == '-') { negative = true; ++idx; }
else if (s[idx] == '+') { ++idx; }
if (s[idx] < '0' || s[idx] > '9')
return false;
int value = 0;
while (s[idx] >= '0' && s[idx] <= '9') {
value = value * 10 + (s[idx] - '0');
++idx;
}
(void)negative; // sign doesn't matter — non-zero is non-zero
return value != 0;
}
// `C_CVar.GetCVarBool(cvar)` — looks the cvar up directly through
// the engine's by-name hash table at `FUN_FIND_CVAR`, reads the
// value string at `+0x20`, coerces to bool. Returns `nil` for
// unknown cvars so callers can distinguish "missing" from "set to
// false" — same shape as modern `C_CVar.GetCVarBool`.
//
// Going through `Script_GetCVar` would work too but adds a Lua-stack
// roundtrip and raises a Lua error for unknown cvars
// (`"CVar \"%s\" doesn't exist."`). The direct lookup lets us turn
// that into the nil return cleanly.
int __fastcall Script_C_CVar_GetCVarBool(void *L) {
if (!Game::Lua::IsString(L, 1)) {
Game::Lua::Error(L, "Usage: C_CVar.GetCVarBool(\"cvar\")");
return 0;
}
const char *name = Game::Lua::ToString(L, 1);
auto findCVar = reinterpret_cast<FindCVar_t>(Offsets::FUN_FIND_CVAR);
const uint8_t *cvar = findCVar(name);
if (cvar == nullptr) {
Game::Lua::PushNil(L);
return 1;
}
const char *value = *reinterpret_cast<const char *const *>(
cvar + Offsets::OFF_CVAR_VALUE_STR);
Game::Lua::PushBool(L, StringToBool(value));
return 1;
}
void RegisterLuaFunctions() {
Game::Lua::RegisterTableFunction("C_CVar", "GetCVarBool",
&Script_C_CVar_GetCVarBool);
}
// Same surface on the glue Lua state — CVar storage is process-global
// so the polyfill works identically pre-login. `RegisterTableFunction`
// reads `VAR_LUA_STATE` to pick which state it writes to, which during
// the glue init hook points at the glue state.
void RegisterGlueFunctions() {
Game::Lua::RegisterTableFunction("C_CVar", "GetCVarBool",
&Script_C_CVar_GetCVarBool);
}
const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
const Game::GlueModuleAutoRegister _glueAutoreg{&RegisterGlueFunctions};
} // namespace
} // namespace CVar::Bool