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
|
#include "pch.h"
#include "buildainfile.h"
#include "convar.h"
#include "hookutils.h"
#include <fstream>
const int AINET_VERSION_NUMBER = 57;
const int AINET_SCRIPT_VERSION_NUMBER = 21;
const int MAP_VERSION_TEMP = 30;
const int PLACEHOLDER_CRC = 0;
const int MAX_HULLS = 5;
struct CAI_NodeLink
{
short srcId;
short destId;
bool hulls[MAX_HULLS];
char unk0;
char unk1; // maps => unk0 on disk
char unk2[5];
int64_t flags;
};
#pragma pack (push, 1)
struct CAI_NodeLinkDisk
{
short srcId;
short destId;
char unk0;
bool hulls[MAX_HULLS];
};
struct CAI_Node
{
int index; // not present on disk
float x;
float y;
float z;
float hulls[MAX_HULLS];
float yaw;
int unk0; // always 2 in buildainfile, maps directly to unk0 in disk struct
int unk1; // maps directly to unk1 in disk struct
int unk2[MAX_HULLS]; // maps directly to unk2 in disk struct, despite being ints rather than shorts
// view server.dll+393672 for context and death wish
char unk3[MAX_HULLS]; // hell on earth, should map to unk3 on disk
char pad[3]; // aligns next bytes
float unk4[MAX_HULLS]; // i have no fucking clue, calculated using some kind of demon hell function float magic
CAI_NodeLink** links;
char unk5[16];
int linkcount;
int unk11; // bad name lmao
short unk6; // should match up to unk4 on disk
char unk7[16]; // padding until next bit
short unk8; // should match up to unk5 on disk
char unk9[8]; // padding until next bit
char unk10[8]; // should match up to unk6 on disk
};
// the way CAI_Nodes are represented in on-disk ain files
#pragma pack (push, 1)
struct CAI_NodeDisk
{
float x;
float y;
float z;
float yaw;
float hulls[MAX_HULLS];
char unk0;
int unk1;
short unk2[MAX_HULLS];
char unk3[MAX_HULLS];
short unk4;
short unk5;
char unk6[8];
}; // total size of 68 bytes
struct CAI_Network
{
// +0
char unk0[8];
// +8
int linkcount; // this is uninitialised and never set on ain build, fun!
// +12
char unk1[124];
// +136
int zonecount;
// +140
char unk2[24];
// +164
int hintcount;
// +168
char unk3[4000];
// +4168
int scriptnodecount;
// +4172
char unk4[80000];
// +84172
int nodecount;
// +84176
CAI_Node** nodes;
};
ConVar* Cvar_ns_ai_dumpAINfileFromLoad;
void DumpAINInfo(CAI_Network* aiNetwork)
{
// dump from memory
spdlog::info("writing .ain to file....");
spdlog::info("");
spdlog::info("");
spdlog::info("");
spdlog::info("");
spdlog::info("");
std::ofstream writeStream("dumped_ain.ain", std::ofstream::binary);
spdlog::info("writing ainet version: {}", AINET_VERSION_NUMBER);
writeStream.write((char*)&AINET_VERSION_NUMBER, sizeof(int));
spdlog::info("writing map version: {}", MAP_VERSION_TEMP); // temp
writeStream.write((char*)&MAP_VERSION_TEMP, sizeof(int));
spdlog::info("writing placeholder crc: {}", PLACEHOLDER_CRC);
writeStream.write((char*)&PLACEHOLDER_CRC, sizeof(int));
int calculatedLinkcount = 0;
// path nodes
spdlog::info("writing nodecount: {}", aiNetwork->nodecount);
writeStream.write((char*)&aiNetwork->nodecount, sizeof(int));
for (int i = 0; i < aiNetwork->nodecount; i++)
{
// construct on-disk node struct
CAI_NodeDisk diskNode;
diskNode.x = aiNetwork->nodes[i]->x;
diskNode.y = aiNetwork->nodes[i]->y;
diskNode.z = aiNetwork->nodes[i]->z;
diskNode.yaw = aiNetwork->nodes[i]->yaw;
memcpy(diskNode.hulls, aiNetwork->nodes[i]->hulls, sizeof(diskNode.hulls));
diskNode.unk0 = (char)aiNetwork->nodes[i]->unk0;
diskNode.unk1 = aiNetwork->nodes[i]->unk1;
for (int j = 0; j < MAX_HULLS; j++)
{
diskNode.unk2[j] = (short)aiNetwork->nodes[i]->unk2[j];
spdlog::info((short)aiNetwork->nodes[i]->unk2[j]);
}
memcpy(diskNode.unk3, aiNetwork->nodes[i]->unk3, sizeof(diskNode.unk3));
diskNode.unk4 = aiNetwork->nodes[i]->unk6;
diskNode.unk5 = -1; // aiNetwork->nodes[i]->unk8; // this field is wrong, however, it's always -1 in vanilla navmeshes anyway, so no biggie
memcpy(diskNode.unk6, aiNetwork->nodes[i]->unk10, sizeof(diskNode.unk6));
spdlog::info("writing node {} from {} to {:x}", aiNetwork->nodes[i]->index, (void*)aiNetwork->nodes[i], writeStream.tellp());
writeStream.write((char*)&diskNode, sizeof(CAI_NodeDisk));
calculatedLinkcount += aiNetwork->nodes[i]->linkcount;
}
spdlog::info("linkcount: {}", aiNetwork->linkcount);
spdlog::info("calculated total linkcount: {}", calculatedLinkcount);
calculatedLinkcount /= 2;
if (Cvar_ns_ai_dumpAINfileFromLoad->m_nValue)
{
if (aiNetwork->linkcount == calculatedLinkcount)
spdlog::info("caculated linkcount is normal!");
else
spdlog::warn("calculated linkcount has weird value! this is expected on build!");
}
spdlog::info("writing linkcount: {}", calculatedLinkcount);
writeStream.write((char*)&calculatedLinkcount, sizeof(int));
for (int i = 0; i < aiNetwork->nodecount; i++)
{
for (int j = 0; j < aiNetwork->nodes[i]->linkcount; j++)
{
// skip links that don't originate from current node
if (aiNetwork->nodes[i]->links[j]->srcId != aiNetwork->nodes[i]->index)
continue;
CAI_NodeLinkDisk diskLink;
diskLink.srcId = aiNetwork->nodes[i]->links[j]->srcId;
diskLink.destId = aiNetwork->nodes[i]->links[j]->destId;
diskLink.unk0 = aiNetwork->nodes[i]->links[j]->unk1;
memcpy(diskLink.hulls, aiNetwork->nodes[i]->links[j]->hulls, sizeof(diskLink.hulls));
spdlog::info("writing link {} => {} to {:x}", diskLink.srcId, diskLink.destId, writeStream.tellp());
writeStream.write((char*)&diskLink, sizeof(CAI_NodeLinkDisk));
}
}
// don't know what this is, it's likely a block from tf1 that got deprecated?
spdlog::info("writing {:x} bytes for unknown block at {:x}", aiNetwork->nodecount * sizeof(uint32_t), writeStream.tellp());
uint32_t* unkNodeBlock = new uint32_t[aiNetwork->nodecount];
memset(unkNodeBlock, 0, aiNetwork->nodecount * sizeof(uint32_t));
writeStream.write((char*)unkNodeBlock, aiNetwork->nodecount * sizeof(uint32_t));
delete[] unkNodeBlock;
spdlog::info("hintcount: {}", aiNetwork->hintcount);
spdlog::info("scriptnodecount: {}", aiNetwork->scriptnodecount);
spdlog::info("zonecount: {}", aiNetwork->zonecount);
writeStream.close();
}
typedef void(*CAI_NetworkBuilder__BuildType)(void* builder, CAI_Network* aiNetwork, void* unknown);
CAI_NetworkBuilder__BuildType CAI_NetworkBuilder__Build;
void CAI_NetworkBuilder__BuildHook(void* builder, CAI_Network* aiNetwork, void* unknown)
{
CAI_NetworkBuilder__Build(builder, aiNetwork, unknown);
DumpAINInfo(aiNetwork);
}
typedef void(*LoadAINFileType)(void* aimanager, void* buf, const char* filename);
LoadAINFileType LoadAINFile;
void LoadAINFileHook(void* aimanager, void* buf, const char* filename)
{
LoadAINFile(aimanager, buf, filename);
if (Cvar_ns_ai_dumpAINfileFromLoad->m_nValue)
{
spdlog::info("running DumpAINInfo for loaded file {}", filename);
DumpAINInfo(*(CAI_Network**)((char*)aimanager + 2536));
}
}
void InitialiseBuildAINFileHooks(HMODULE baseAddress)
{
Cvar_ns_ai_dumpAINfileFromLoad = RegisterConVar("ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk");
HookEnabler hook;
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x385E20, &CAI_NetworkBuilder__BuildHook, reinterpret_cast<LPVOID*>(&CAI_NetworkBuilder__Build));
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x3933A0, &LoadAINFileHook, reinterpret_cast<LPVOID*>(&LoadAINFile));
// remove a check that prevents a logging function in link generation from working
// due to the sheer amount of logging this is a massive perf hit to generation, but spewlog_enable 0 exists so whatever
{
void* ptr = (char*)baseAddress + 0x3889B6;
TempReadWrite rw(ptr);
*((char*)ptr) = (char)0x90;
*((char*)ptr + 1) = (char)0x90;
*((char*)ptr + 2) = (char)0x90;
*((char*)ptr + 3) = (char)0x90;
*((char*)ptr + 4) = (char)0x90;
*((char*)ptr + 5) = (char)0x90;
}
{
void* ptr = (char*)baseAddress + 0x3889BF;
TempReadWrite rw(ptr);
*((char*)ptr) = (char)0x90;
*((char*)ptr + 1) = (char)0x90;
*((char*)ptr + 2) = (char)0x90;
*((char*)ptr + 3) = (char)0x90;
*((char*)ptr + 4) = (char)0x90;
*((char*)ptr + 5) = (char)0x90;
}
}
|