-
Notifications
You must be signed in to change notification settings - Fork 1
/
ApiGenerator.as
320 lines (265 loc) · 9.1 KB
/
ApiGenerator.as
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include "generated_props/includes"
#include "special_props/includes"
#include "PvFinder"
enum field_types {
FIELD_BOOL,
FIELD_BYTE,
FIELD_SHORT,
FIELD_INT,
FIELD_UINT,
FIELD_FLT,
FIELD_ENTVARS,
FIELD_EHANDLE,
FIELD_INT64,
FIELD_VEC3,
FIELD_STR_T,
FIELD_STR,
FIELD_SCHED,
}
funcdef void pvPropSetter(CBaseEntity@ ent, TestValue);
funcdef TestValue pvPropGetter(CBaseEntity@ ent);
// the result for a single class property
class PvData {
PvProp@ prop;
int offset = -1;
int arrayIdx = 0;
PvData() {}
PvData(PvProp@ prop, int offset, int arrayIdx) {
@this.prop = @prop;
this.offset = offset;
this.arrayIdx = arrayIdx;
}
}
// the final result of a reveresed class, containing all properties ordered by offset
class ReversedData {
string cname;
array<PvData> results;
int childFields; // fields that are not duplicated in the base class
ReversedData() {}
ReversedData(string cname, array<PvData> results, int childFields) {
this.cname = cname;
this.results = results;
this.childFields = childFields;
}
int getClassSize() {
PvData@ lastResult = results[results.size()-1];
return lastResult.offset + lastResult.prop.getSize();
}
}
// like a union, for when the function caller doesn't know the needed data type
class TestValue {
uint8 v8;
uint16 v16;
uint32 v32;
uint64 v64;
float vf;
Vector vec3;
string_t str_t;
string str;
entvars_t@ pev;
EHandle ehandle;
Schedule@ sched;
TestValue() {}
TestValue(int8 v) { v8 = v; }
TestValue(uint8 v) { v8 = v; }
TestValue(int16 v) { v16 = v; }
TestValue(uint16 v) { v16 = v; }
TestValue(int v) { v32 = v; }
TestValue(uint v) { v32 = v; }
TestValue(int64 v) { v64 = v; }
TestValue(uint64 v) { v64 = v; }
TestValue(float v) { vf = v; }
TestValue(Vector v) { vec3 = v; }
TestValue(string_t v) { str_t = v; }
TestValue(string v) { str = v; }
TestValue(entvars_t@ v) { @pev = @v; }
TestValue(EHandle v) { ehandle = v; }
TestValue(Schedule@ v) { @sched = @v; }
TestValue(bool setNotEmpty) {
if (setNotEmpty) {
v8 = 0x01;
v16 = 0x1234;
v32 = 0x12345678;
v64 = 0x12345678ABCDEF01;
vf = 1234.567f;
vec3 = Vector(1234.567f, 2235.567f, 3234.567f);
str_t = "1234567890";
str = "1234567890";
@pev = @g_EntityFuncs.Instance(0).pev;
ehandle = EHandle(g_EntityFuncs.Instance(0));
@sched = cast<CBasePlayer@>(g_EntityFuncs.Instance(1)).GetSchedule();
} // else 0s
}
}
// info needed to find a single class property
class PvProp {
string name;
int fieldtype;
int arraySize = 1;
string desc;
pvPropSetter@ setter;
pvPropGetter@ getter;
PvProp() {}
PvProp( string name, int fieldtype, string desc, pvPropGetter@ getter, pvPropSetter@ setter) {
this.fieldtype = fieldtype;
this.name = name;
this.desc = desc;
@this.setter = @setter;
@this.getter = @getter;
if (int(name.Find("[")) != -1) {
string sz = name.SubString(name.Find("[")+1);
sz = sz.SubString(0, name.Find("]")-1);
arraySize = atoi(sz);
}
}
int getSize() {
return getTypeSize() * arraySize;
}
int getTypeSize() {
switch(fieldtype) {
case FIELD_BOOL: return 1;
case FIELD_BYTE: return 1;
case FIELD_SHORT: return 2;
case FIELD_INT: return 4;
case FIELD_UINT: return 4;
case FIELD_FLT: return 4;
case FIELD_ENTVARS: return 4;
case FIELD_EHANDLE: return 8;
case FIELD_INT64: return 8;
case FIELD_VEC3: return 4*3;
case FIELD_STR_T: return 4;
case FIELD_STR: return 4;
case FIELD_SCHED: return 4;
}
return 0;
}
// for writing the header file
string ctype() {
switch(fieldtype) {
case FIELD_BOOL: return "bool";
case FIELD_BYTE: return "byte";
case FIELD_SHORT: return "short";
case FIELD_INT: return "int";
case FIELD_UINT: return "unsigned int";
case FIELD_FLT: return "float";
case FIELD_ENTVARS: return "entvars_t*";
case FIELD_EHANDLE: return "EHandle";
case FIELD_INT64: return "uint64_t";
case FIELD_VEC3: return "vec3_t";
case FIELD_STR_T: return "string_t";
case FIELD_STR: return "string_t";
case FIELD_SCHED: return "void*";
}
return 0;
}
// angelscript can't size/value validate these
bool isPointer() {
switch(fieldtype) {
case FIELD_ENTVARS: return true;
case FIELD_EHANDLE: return true;
case FIELD_SCHED: return true;
}
return arraySize > 1;
}
}
void print(string text) { g_Game.AlertMessage( at_console, text); }
void println(string text) { print(text + "\n"); }
CClientCommand _apigen("apigen", "Generate metamod API", @apiGen );
CClientCommand _apitest("apitest", "Generate metamod API", @apiTest );
CConCommand@ apiResultCmd = @CConCommand( "private_api_result", "metamod response to a private api test", @private_api_result );
// scan results from the metamod plugin
int g_pv_offset = -1; // where data was changed
int g_pv_size = -1; // size of data changed
uint64 g_pv_result = 0; // value found, as an integer
float g_pv_result_f = 0; // value found, as a float
Vector g_pv_result_vec3 = Vector(0,0,0); // value(s) found, as a vector
int g_arr_idx = 0; // array index to test
int g_classid = 0; // make sure unknown data has unique names when inheriting
array<ReversedData> g_reversed_data; // used to exclude classes that are duplicates for CBaseEntity
void PluginInit() {
g_Module.ScriptInfo.SetAuthor( "w00tguy" );
g_Module.ScriptInfo.SetContactInfo( "github" );
}
void MapInit() {
g_Game.PrecacheOther("func_door");
g_Game.PrecacheOther("item_inventory");
}
// metamod calls this after scanning private entity data for changes
void private_api_result( const CCommand@ args ) {
CBasePlayer@ plr = g_ConCommandSystem.GetCurrentPlayer();
if (args.ArgC() == 0) {
g_pv_result = 0;
g_pv_result_f = 0;
g_pv_offset = -1;
g_pv_size = -1;
return;
}
g_pv_result = atoui64(args[1], 10);
g_pv_result_f = atof(args[2]);
array<string> vec3_parts = args[3].Split("_");
g_pv_result_vec3.x = atof(vec3_parts[0]);
g_pv_result_vec3.y = atof(vec3_parts[1]);
g_pv_result_vec3.z = atof(vec3_parts[2]);
g_pv_offset = atoi(args[4]);
g_pv_size = atoi(args[5]);
}
void failsafe_ent_remove(EHandle h_ent) {
g_EntityFuncs.Remove(h_ent);
}
void generateApis(CBasePlayer@ plr) {
g_reversed_data.resize(0);
// order is important here, it takes some guess work to figure out the hierarchy.
// if classes have duplicate fields between them then this probably needs updating.
// TODO: just do this in 2 passes. first one finds the field offsets, 2nd writes the
// headers with all classes searchable as parents.
PvFinder(plr, "CBaseEntity", CBaseEntityPv, null);
PvFinder("trigger_relay", "CBaseDelay", CBaseDelayPv, null);
PvFinder(plr, "CBaseAnimating", CBaseAnimatingPv, null);
PvFinder("func_door", "CBaseToggle", CBaseTogglePv, null);
PvFinder("func_button", "CBaseButton", CBaseButtonPv, null);
PvFinder("func_door", "CBaseDoor", CBaseDoorPv, null);
PvFinder(plr, "CBaseMonster", CBaseMonsterPv, null);
PvFinder(plr, "CBasePlayer", CBasePlayerPv, CBasePlayerPv_Special);
PvFinder("scripted_sequence", "CCineMonster", CCineMonsterPv, null);
PvFinder("grenade", "CGrenade", CGrenadePv, null);
PvFinder("weapon_crowbar", "CBasePlayerItem", CBasePlayerItemPv, null);
PvFinder("weapon_crowbar", "CBasePlayerWeapon", CBasePlayerWeaponPv, null);
PvFinder("ammo_357", "CBasePlayerAmmo", CBasePlayerAmmoPv, null);
PvFinder("func_tank", "CBaseTank", CBaseTankPv, null);
PvFinder("item_inventory", "CItemInventory", CItemInventoryPv, null);
PvFinder("item_battery", "CItem", CItemPv, null);
PvFinder("path_track", "CPathTrack", CPathTrackPv, null);
PvFinder("env_beam", "CBeam", CBeamPv, null);
PvFinder("env_laser", "CLaser", CLaserPv, null);
string outputFile = "scripts/plugins/store/ApiGenerator/private_api.h";
File@ f = g_FileSystem.OpenFile( outputFile, OpenFile::WRITE);
if( f is null || !f.IsOpen() ) {
println("Failed to open file for writing: " + outputFile);
println("Create the 'ApiGenerator' folder if it doesn't exist.");
return;
}
f.Write("// This code was automatically generated by the ApiGenerator plugin.\n\n");
f.Write("#include \"EHandle.h\"\n");
for (uint i = 0; i < g_reversed_data.size(); i++) {
f.Write("#include \"" + g_reversed_data[i].cname + ".h\"\n");
}
f.Close();
println("\nFinished writing private APIs for " + g_reversed_data.size() + " classes.");
}
void apiTestLoop(EHandle h_plr) {
CBasePlayer@ plr = cast<CBasePlayer@>(h_plr.GetEntity());
if (plr is null) {
return;
}
//CItemInventory@ testEnt = cast<CItemInventory@>(g_EntityFuncs.CreateEntity("item_inventory", null, true));
//testEnt.m_szDisplayName = "test";
//g_EntityFuncs.Remove(testEnt);
}
void apiTest( const CCommand@ args ) {
CBasePlayer@ plr = g_ConCommandSystem.GetCurrentPlayer();
g_Scheduler.SetInterval("apiTestLoop", 0.1f, -1, EHandle(plr));
}
void apiGen( const CCommand@ args ) {
CBasePlayer@ plr = g_ConCommandSystem.GetCurrentPlayer();
generateApis(plr);
}