extensions/sdktools/vhelpers.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4319 f17d11bcd078
permissions -rw-r--r--
Update CS:GO team score gamedata.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2010 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "extension.h"
    33 #include "util.h"
    34 #include "vhelpers.h"
    35 #include "vglobals.h"
    36 
    37 CallHelper s_Teleport;
    38 CallHelper s_GetVelocity;
    39 CallHelper s_EyeAngles;
    40 
    41 class CTraceFilterSimple : public CTraceFilterEntitiesOnly
    42 {
    43 public:
    44 	CTraceFilterSimple(const IHandleEntity *passentity): m_pPassEnt(passentity)
    45 	{
    46 	}
    47 	virtual bool ShouldHitEntity(IHandleEntity *pServerEntity, int contentsMask)
    48 	{
    49 		if (pServerEntity == m_pPassEnt)
    50 		{
    51 			return false;
    52 		}
    53 		return true;
    54 	}
    55 private:
    56 	const IHandleEntity *m_pPassEnt;
    57 };
    58 
    59 bool SetupTeleport()
    60 {
    61 	if (s_Teleport.setup)
    62 	{
    63 		return s_Teleport.supported;
    64 	}
    65 
    66 	/* Setup Teleport */
    67 	int offset;
    68 	if (g_pGameConf->GetOffset("Teleport", &offset))
    69 	{
    70 		PassInfo info[3];
    71 		info[0].flags = info[1].flags = info[2].flags = PASSFLAG_BYVAL;
    72 		info[0].size = info[1].size = info[2].size = sizeof(void *);
    73 		info[0].type = info[1].type = info[2].type = PassType_Basic;
    74 
    75 		s_Teleport.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 3);
    76 
    77 		if (s_Teleport.call != NULL)
    78 		{
    79 			s_Teleport.supported = true;
    80 		}
    81 	}
    82 
    83 	s_Teleport.setup = true;
    84 
    85 	return s_Teleport.supported;
    86 }
    87 
    88 void Teleport(CBaseEntity *pEntity, Vector *origin, QAngle *ang, Vector *velocity)
    89 {
    90 	unsigned char params[sizeof(void *) * 4];
    91 	unsigned char *vptr = params;
    92 	*(CBaseEntity **)vptr = pEntity;
    93 	vptr += sizeof(CBaseEntity *);
    94 	*(Vector **)vptr = origin;
    95 	vptr += sizeof(Vector *);
    96 	*(QAngle **)vptr = ang;
    97 	vptr += sizeof(QAngle *);
    98 	*(Vector **)vptr = velocity;
    99 	
   100 	s_Teleport.call->Execute(params, NULL);
   101 }
   102 
   103 bool IsTeleportSupported()
   104 {
   105 	return SetupTeleport();
   106 }
   107 
   108 bool SetupGetVelocity()
   109 {
   110 	if (s_GetVelocity.setup)
   111 	{
   112 		return s_GetVelocity.supported;
   113 	}
   114 
   115 	int offset;
   116 	if (g_pGameConf->GetOffset("GetVelocity", &offset))
   117 	{
   118 		PassInfo info[2];
   119 		info[0].flags = info[1].flags = PASSFLAG_BYVAL;
   120 		info[0].size = info[1].size = sizeof(void *);
   121 		info[0].type = info[1].type = PassType_Basic;
   122 
   123 		s_GetVelocity.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 2);
   124 
   125 		if (s_GetVelocity.call != NULL)
   126 		{
   127 			s_GetVelocity.supported = true;
   128 		}
   129 	}
   130 
   131 	s_GetVelocity.setup = true;
   132 
   133 	return s_GetVelocity.supported;
   134 }
   135 
   136 void GetVelocity(CBaseEntity *pEntity, Vector *velocity, AngularImpulse *angvelocity)
   137 {
   138 	unsigned char params[sizeof(void *) * 3];
   139 	unsigned char *vptr = params;
   140 	*(CBaseEntity **)vptr = pEntity;
   141 	vptr += sizeof(CBaseEntity *);
   142 	*(Vector **)vptr = velocity;
   143 	vptr += sizeof(Vector *);
   144 	*(AngularImpulse **)vptr = angvelocity;
   145 
   146 	s_GetVelocity.call->Execute(params, NULL);
   147 }
   148 
   149 bool IsGetVelocitySupported()
   150 {
   151 	return SetupGetVelocity();
   152 }
   153 
   154 bool SetupGetEyeAngles()
   155 {
   156 	if (s_EyeAngles.setup)
   157 	{
   158 		return s_EyeAngles.supported;
   159 	}
   160 
   161 	int offset;
   162 	if (g_pGameConf->GetOffset("EyeAngles", &offset))
   163 	{
   164 		PassInfo info[1];
   165 		info[0].flags = PASSFLAG_BYVAL;
   166 		info[0].size = sizeof(void *);
   167 		info[0].type = PassType_Basic;
   168 
   169 		s_EyeAngles.call = g_pBinTools->CreateVCall(offset, 0, 0, info, NULL, 0);
   170 
   171 		if (s_EyeAngles.call != NULL)
   172 		{
   173 			s_EyeAngles.supported = true;
   174 		}
   175 	}
   176 
   177 	s_EyeAngles.setup = true;
   178 
   179 	return s_EyeAngles.supported;
   180 }
   181 
   182 bool GetEyeAngles(CBaseEntity *pEntity, QAngle *pAngles)
   183 {
   184 	if (!IsEyeAnglesSupported())
   185 	{
   186 		return false;
   187 	}
   188 
   189 	QAngle *pRetAngle = NULL;
   190 	unsigned char params[sizeof(void *)];
   191 	unsigned char *vptr = params;
   192 
   193 	*(CBaseEntity **)vptr = pEntity;
   194 
   195 	s_EyeAngles.call->Execute(params, &pRetAngle);
   196 
   197 	if (pRetAngle == NULL)
   198 	{
   199 		return false;
   200 	}
   201 
   202 	*pAngles = *pRetAngle;
   203 
   204 	return true;
   205 }
   206 
   207 int GetClientAimTarget(edict_t *pEdict, bool only_players)
   208 {
   209 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   210 
   211 	if (pEntity == NULL)
   212 	{
   213 		return -1;
   214 	}
   215 
   216 	Vector eye_position;
   217 	QAngle eye_angles;
   218 
   219 	/* Get the private information we need */
   220 #if SOURCE_ENGINE == SE_DOTA
   221 	serverClients->ClientEarPosition(IndexOfEdict(pEdict), &eye_position);
   222 #else
   223 	serverClients->ClientEarPosition(pEdict, &eye_position);
   224 #endif
   225 
   226 	if (!GetEyeAngles(pEntity, &eye_angles))
   227 	{
   228 		return -2;
   229 	}
   230 
   231 	Vector aim_dir;
   232 	AngleVectors(eye_angles, &aim_dir);
   233 	VectorNormalize(aim_dir);
   234 
   235 	Vector vec_end = eye_position + aim_dir * 8000;
   236 
   237 	Ray_t ray;
   238 	ray.Init(eye_position, vec_end);
   239 
   240 	trace_t tr;
   241 	CTraceFilterSimple simple(pEdict->GetIServerEntity());
   242 
   243 	enginetrace->TraceRay(ray, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, &simple, &tr);
   244 
   245 	if (tr.fraction == 1.0f || tr.m_pEnt == NULL)
   246 	{
   247 		return -1;
   248 	}
   249 
   250 	int ent_ref = gamehelpers->EntityToBCompatRef(tr.m_pEnt);
   251 	int ent_index = gamehelpers->ReferenceToIndex(ent_ref);
   252 
   253 	IGamePlayer *pTargetPlayer = playerhelpers->GetGamePlayer(ent_index);
   254 	if (pTargetPlayer != NULL && !pTargetPlayer->IsInGame())
   255 	{
   256 		return -1;
   257 	}
   258 	else if (only_players && pTargetPlayer == NULL)
   259 	{
   260 		return -1;
   261 	}
   262 
   263 	return ent_ref;
   264 }
   265 
   266 bool IsEyeAnglesSupported()
   267 {
   268 	return SetupGetEyeAngles();
   269 }
   270 
   271 bool GetPlayerInfo(int client, player_info_t *info)
   272 {
   273 #if SOURCE_ENGINE >= SE_ORANGEBOX
   274 	return engine->GetPlayerInfo(client, info);
   275 #else
   276 	return (iserver) ? iserver->GetPlayerInfo(client-1, info) : false;
   277 #endif
   278 }
   279 
   280 void ShutdownHelpers()
   281 {
   282 	s_Teleport.Shutdown();
   283 	s_GetVelocity.Shutdown();
   284 	s_EyeAngles.Shutdown();
   285 }
   286 
   287 bool FindNestedDataTable(SendTable *pTable, const char *name)
   288 {
   289 	if (strcmp(pTable->GetName(), name) == 0)
   290 	{
   291 		return true;
   292 	}
   293 
   294 	int props = pTable->GetNumProps();
   295 	SendProp *prop;
   296 
   297 	for (int i=0; i<props; i++)
   298 	{
   299 		prop = pTable->GetProp(i);
   300 		if (prop->GetDataTable())
   301 		{
   302 			if (FindNestedDataTable(prop->GetDataTable(), name))
   303 			{
   304 				return true;
   305 			}
   306 		}
   307 	}
   308 
   309 	return false;
   310 }
   311 
   312 char *UTIL_SendFlagsToString(int flags, int type)
   313 {
   314 	static char str[1024];
   315 	str[0] = 0;
   316 
   317 	if (flags & SPROP_UNSIGNED)
   318 	{
   319 		strcat(str, "Unsigned|");
   320 	}
   321 	if (flags & SPROP_COORD)
   322 	{
   323 		strcat(str, "Coord|");
   324 	}
   325 	if (flags & SPROP_NOSCALE)
   326 	{
   327 		strcat(str, "NoScale|");
   328 	}
   329 	if (flags & SPROP_ROUNDDOWN)
   330 	{
   331 		strcat(str, "RoundDown|");
   332 	}
   333 	if (flags & SPROP_ROUNDUP)
   334 	{
   335 		strcat(str, "RoundUp|");
   336 	}
   337 	if (flags & SPROP_NORMAL)
   338 	{
   339 		if (type == DPT_Int)
   340 		{
   341 			strcat(str, "VarInt|");
   342 		}
   343 		else
   344 		{
   345 			strcat(str, "Normal|");
   346 		}
   347 	}
   348 	if (flags & SPROP_EXCLUDE)
   349 	{
   350 		strcat(str, "Exclude|");
   351 	}
   352 	if (flags & SPROP_XYZE)
   353 	{
   354 		strcat(str, "XYZE|");
   355 	}
   356 	if (flags & SPROP_INSIDEARRAY)
   357 	{
   358 		strcat(str, "InsideArray|");
   359 	}
   360 	if (flags & SPROP_PROXY_ALWAYS_YES)
   361 	{
   362 		strcat(str, "AlwaysProxy|");
   363 	}
   364 	if (flags & SPROP_CHANGES_OFTEN)
   365 	{
   366 		strcat(str, "ChangesOften|");
   367 	}
   368 	if (flags & SPROP_IS_A_VECTOR_ELEM)
   369 	{
   370 		strcat(str, "VectorElem|");
   371 	}
   372 	if (flags & SPROP_COLLAPSIBLE)
   373 	{
   374 		strcat(str, "Collapsible|");
   375 	}
   376 #if SOURCE_ENGINE >= SE_ORANGEBOX
   377 	if (flags & SPROP_COORD_MP)
   378 	{
   379 		strcat(str, "CoordMP|");
   380 	}
   381 	if (flags & SPROP_COORD_MP_LOWPRECISION)
   382 	{
   383 		strcat(str, "CoordMPLowPrec|");
   384 	}
   385 	if (flags & SPROP_COORD_MP_INTEGRAL)
   386 	{
   387 		strcat(str, "CoordMpIntegral|");
   388 	}
   389 #endif
   390 
   391 	int len = strlen(str) - 1;
   392 	if (len > 0)
   393 	{
   394 		str[len] = 0; // Strip the final '|'
   395 	}
   396 
   397 	return str;
   398 }
   399 
   400 void UTIL_DrawSendTable_XML(FILE *fp, SendTable *pTable, int space_count)
   401 {
   402 	char spaces[255];
   403 
   404 	for (int i = 0; i < space_count; i++)
   405 	{
   406 		spaces[i] = ' ';
   407 	}
   408 	spaces[space_count] = '\0';
   409 
   410 	const char *type_name;
   411 	SendTable *pOtherTable;
   412 	SendProp *pProp;
   413 
   414 	fprintf(fp, " %s<sendtable name=\"%s\">\n", spaces, pTable->GetName());
   415 	for (int i = 0; i < pTable->GetNumProps(); i++)
   416 	{
   417 		pProp = pTable->GetProp(i);
   418 
   419 		fprintf(fp, "  %s<property name=\"%s\">\n", spaces, pProp->GetName());
   420 
   421 		if ((type_name = GetDTTypeName(pProp->GetType())) != NULL)
   422 		{
   423 			fprintf(fp, "   %s<type>%s</type>\n", spaces, type_name);
   424 		}
   425 		else
   426 		{
   427 			fprintf(fp, "   %s<type>%d</type>\n", spaces, pProp->GetType());
   428 		}
   429 
   430 		fprintf(fp, "   %s<offset>%d</offset>\n", spaces, pProp->GetOffset());
   431 		fprintf(fp, "   %s<bits>%d</bits>\n", spaces, pProp->m_nBits);
   432 		fprintf(fp, "   %s<flags>%s</flags>\n", spaces, UTIL_SendFlagsToString(pProp->GetFlags(), pProp->GetType()));
   433 
   434 		if ((pOtherTable = pTable->GetProp(i)->GetDataTable()) != NULL)
   435 		{
   436 			UTIL_DrawSendTable_XML(fp, pOtherTable, space_count + 3);
   437 		}
   438 
   439 		fprintf(fp, "  %s</property>\n", spaces);
   440 	}
   441 	fprintf(fp, " %s</sendtable>\n", spaces);
   442 }
   443 
   444 void UTIL_DrawServerClass_XML(FILE *fp, ServerClass *sc)
   445 {
   446 	fprintf(fp, "<serverclass name=\"%s\">\n", sc->GetName());
   447 	UTIL_DrawSendTable_XML(fp, sc->m_pTable, 0);
   448 	fprintf(fp, "</serverclass>\n");
   449 }
   450 
   451 void UTIL_DrawSendTable(FILE *fp, SendTable *pTable, int level = 1)
   452 {
   453 	SendProp *pProp;
   454 	const char *type;
   455 
   456 	for (int i = 0; i < pTable->GetNumProps(); i++)
   457 	{
   458 		pProp = pTable->GetProp(i);
   459 		if (pProp->GetDataTable())
   460 		{
   461 			fprintf(fp, "%*sTable: %s (offset %d) (type %s)\n", 
   462 				level, "", 
   463 				pProp->GetName(), 
   464 				pProp->GetOffset(), 
   465 				pProp->GetDataTable()->GetName());
   466 			
   467 			UTIL_DrawSendTable(fp, pProp->GetDataTable(), level + 1);
   468 		}
   469 		else
   470 		{
   471 			type = GetDTTypeName(pProp->GetType());
   472 
   473 			if (type != NULL)
   474 			{
   475 				fprintf(fp,
   476 					"%*sMember: %s (offset %d) (type %s) (bits %d) (%s)\n", 
   477 					level, "", 
   478 					pProp->GetName(),
   479 					pProp->GetOffset(),
   480 					type,
   481 					pProp->m_nBits,
   482 					UTIL_SendFlagsToString(pProp->GetFlags(), pProp->GetType()));
   483 			}
   484 			else
   485 			{
   486 				fprintf(fp,
   487 					"%*sMember: %s (offset %d) (type %d) (bits %d) (%s)\n", 
   488 					level, "", 
   489 					pProp->GetName(),
   490 					pProp->GetOffset(),
   491 					pProp->GetType(),
   492 					pProp->m_nBits,
   493 					UTIL_SendFlagsToString(pProp->GetFlags(), pProp->GetType()));
   494 			}
   495 		}
   496 	}
   497 }
   498 
   499 CON_COMMAND(sm_dump_netprops_xml, "Dumps the networkable property table as an XML file")
   500 {
   501 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   502 	CCommand args;
   503 #endif
   504 
   505 	if (args.ArgC() < 2)
   506 	{
   507 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   508 		return;
   509 	}
   510 
   511 	const char *file = args.Arg(1);
   512 	if (!file || file[0] == '\0')
   513 	{
   514 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   515 		return;
   516 	}
   517 
   518 	char path[PLATFORM_MAX_PATH];
   519 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   520 
   521 	FILE *fp = NULL;
   522 	if ((fp = fopen(path, "wt")) == NULL)
   523 	{
   524 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   525 		return;
   526 	}
   527 
   528 	fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
   529 	fprintf(fp, "<!-- Dump of all network properties for \"%s\" follows -->\n\n", g_pSM->GetGameFolderName());
   530 
   531 	ServerClass *pBase = gamedll->GetAllServerClasses();
   532 	while (pBase != NULL)
   533 	{
   534 		UTIL_DrawServerClass_XML(fp, pBase);
   535 		pBase = pBase->m_pNext;
   536 	}
   537 
   538 	fclose(fp);
   539 }
   540 
   541 CON_COMMAND(sm_dump_netprops, "Dumps the networkable property table as a text file")
   542 {
   543 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   544 	CCommand args;
   545 #endif
   546 
   547 	if (args.ArgC() < 2)
   548 	{
   549 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   550 		return;
   551 	}
   552 
   553 	const char *file = args.Arg(1);
   554 	if (!file || file[0] == '\0')
   555 	{
   556 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   557 		return;
   558 	}
   559 
   560 	char path[PLATFORM_MAX_PATH];
   561 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   562 
   563 	FILE *fp = NULL;
   564 	if ((fp = fopen(path, "wt")) == NULL)
   565 	{
   566 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   567 		return;
   568 	}
   569 
   570 	fprintf(fp, "// Dump of all network properties for \"%s\" follows\n//\n\n", g_pSM->GetGameFolderName());
   571 
   572 	ServerClass *pBase = gamedll->GetAllServerClasses();
   573 	while (pBase != NULL)
   574 	{
   575 		fprintf(fp, "%s (type %s)\n", pBase->GetName(), pBase->m_pTable->GetName());
   576 		UTIL_DrawSendTable(fp, pBase->m_pTable);
   577 		pBase = pBase->m_pNext;
   578 	}
   579 
   580 	fclose(fp);
   581 }
   582 
   583 #if defined SUBPLATFORM_SECURECRT
   584 void _ignore_invalid_parameter(
   585 						const wchar_t * expression,
   586 						const wchar_t * function, 
   587 						const wchar_t * file, 
   588 						unsigned int line,
   589 						uintptr_t pReserved
   590 						)
   591 {
   592 	/* Wow we don't care, thanks Microsoft. */
   593 }
   594 #endif
   595 
   596 CEntityFactoryDictionary *GetEntityFactoryDictionary()
   597 {
   598 	static CEntityFactoryDictionary *dict = NULL;
   599 
   600 #if SOURCE_ENGINE == SE_TF2 \
   601 	|| SOURCE_ENGINE == SE_CSS \
   602 	|| SOURCE_ENGINE == SE_DODS \
   603 	|| SOURCE_ENGINE == SE_HL2DM \
   604 	|| SOURCE_ENGINE == SE_SDK2013 \
   605 	|| SOURCE_ENGINE == SE_NUCLEARDAWN
   606 	dict = (CEntityFactoryDictionary *) servertools->GetEntityFactoryDictionary();
   607 #else
   608 	if (dict == NULL)
   609 	{
   610 		void *addr;
   611 		if (g_pGameConf->GetMemSig("EntityFactoryFinder", (void **) &addr) && addr)
   612 		{
   613 			int offset;
   614 			if (!g_pGameConf->GetOffset("EntityFactoryOffset", &offset) || !offset)
   615 			{
   616 				return NULL;
   617 			}
   618 			dict = *reinterpret_cast<CEntityFactoryDictionary **>((intptr_t) addr + offset);
   619 		}
   620 	}
   621 
   622 	if (dict == NULL)
   623 	{
   624 		ICallWrapper *pWrapper = NULL;
   625 
   626 		PassInfo retData;
   627 		retData.flags = PASSFLAG_BYVAL;
   628 		retData.size = sizeof(void *);
   629 		retData.type = PassType_Basic;
   630 
   631 		void *addr;
   632 		if (!g_pGameConf->GetMemSig("EntityFactory", &addr) || !addr)
   633 		{
   634 			int offset;
   635 
   636 			if (!g_pGameConf->GetMemSig("EntityFactoryCaller", &addr) || !addr)
   637 				return NULL;
   638 
   639 			if (!g_pGameConf->GetOffset("EntityFactoryCallOffset", &offset))
   640 				return NULL;
   641 
   642 			// Get relative offset to function
   643 			int32_t funcOffset = *(int32_t *)((intptr_t)addr + offset);
   644 
   645 			// Get real address of function
   646 			// Address of signature + offset of relative offset + sizeof(int32_t) offset + relative offset
   647 			addr = (void *)((intptr_t)addr + offset + 4 + funcOffset);
   648 		}
   649 
   650 		pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retData, NULL, 0);
   651 
   652 		if (pWrapper)
   653 		{
   654 			void *returnData = NULL;
   655 
   656 			pWrapper->Execute(NULL, &returnData);
   657 
   658 			pWrapper->Destroy();
   659 
   660 			if (returnData == NULL)
   661 			{
   662 				return NULL;
   663 			}
   664 
   665 			dict = (CEntityFactoryDictionary *) returnData;
   666 		}
   667 	}
   668 #endif
   669 
   670 	return dict;
   671 }
   672 
   673 CON_COMMAND(sm_dump_classes, "Dumps the class list as a text file")
   674 {
   675 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   676 	CCommand args;
   677 #endif
   678 
   679 	if (args.ArgC() < 2)
   680 	{
   681 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   682 		return;
   683 	}
   684 
   685 	const char *file = args.Arg(1);
   686 	if (!file || file[0] == '\0')
   687 	{
   688 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   689 		return;
   690 	}
   691 
   692 	CEntityFactoryDictionary *dict = GetEntityFactoryDictionary();
   693 	if (dict == NULL)
   694 	{
   695 		META_CONPRINT("Failed to locate function\n");
   696 		return;
   697 	}
   698 
   699 	char path[PLATFORM_MAX_PATH];
   700 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   701 
   702 	FILE *fp = NULL;
   703 	if ((fp = fopen(path, "wt")) == NULL)
   704 	{
   705 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   706 		return;
   707 	}
   708 
   709 	char buffer[80];
   710 	buffer[0] = 0;
   711 
   712 #if defined SUBPLATFORM_SECURECRT
   713 	_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
   714 #endif
   715 
   716 	time_t t = g_pSM->GetAdjustedTime();
   717 	size_t written = strftime(buffer, sizeof(buffer), "%d/%m/%Y", localtime(&t));
   718 
   719 #if defined SUBPLATFORM_SECURECRT
   720 	_set_invalid_parameter_handler(handler);
   721 #endif
   722 
   723 	fprintf(fp, "// Dump of all classes for \"%s\" as at %s\n//\n\n", g_pSM->GetGameFolderName(), buffer);
   724 
   725 	sm_datatable_info_t info;
   726 	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
   727 	{
   728 		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
   729 		ServerClass *sclass = entity->GetServerClass();
   730 		fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i));
   731 		
   732 		if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags", &info))
   733 			continue;
   734 		
   735 		int *eflags = (int *)((char *)entity->GetBaseEntity() + info.actual_offset);
   736 		*eflags |= (1<<0); // EFL_KILLME
   737 	}
   738 
   739 	fclose(fp);
   740 
   741 }
   742 
   743 char *UTIL_DataFlagsToString(int flags)
   744 {
   745 	static char str[1024];
   746 	str[0] = 0;
   747 
   748 	if (flags & FTYPEDESC_GLOBAL)
   749 	{
   750 		strcat(str,	"Global|");
   751 	}
   752 	if (flags & FTYPEDESC_SAVE)
   753 	{
   754 		strcat(str,	"Save|");
   755 	}
   756 	if (flags & FTYPEDESC_KEY)
   757 	{
   758 		strcat(str,	"Key|");
   759 	}
   760 	if (flags & FTYPEDESC_INPUT)
   761 	{
   762 		strcat(str,	"Input|");
   763 	}
   764 	if (flags & FTYPEDESC_OUTPUT)
   765 	{
   766 		strcat(str,	"Output|");
   767 	}
   768 	if (flags & FTYPEDESC_FUNCTIONTABLE)
   769 	{
   770 		strcat(str,	"FunctionTable|");
   771 	}
   772 	if (flags & FTYPEDESC_PTR)
   773 	{
   774 		strcat(str,	"Ptr|");
   775 	}
   776 	if (flags & FTYPEDESC_OVERRIDE)
   777 	{
   778 		strcat(str,	"Override|");
   779 	}
   780 
   781 	int len = strlen(str) - 1;
   782 	if (len > 0)
   783 	{
   784 		str[len] = 0; // Strip the final '|'
   785 	}
   786 
   787 	return str;
   788 }
   789 
   790 void UTIL_DrawDataTable(FILE *fp, datamap_t *pMap, int level)
   791 {
   792 	char spaces[255];
   793 
   794 	for (int i=0; i<level; i++)
   795 	{
   796 		spaces[i] = ' ';
   797 	}
   798 
   799 	spaces[level] = '\0';
   800 
   801 	const char *externalname;
   802 	char *flags;
   803 
   804 	while (pMap)
   805 	{
   806 		for (int i=0; i<pMap->dataNumFields; i++)
   807 		{
   808 			if (pMap->dataDesc[i].fieldName == NULL)
   809 			{
   810 				continue;
   811 			}
   812 
   813 			if (pMap->dataDesc[i].td)
   814 			{
   815 				fprintf(fp, " %sSub-Class Table (%d Deep): %s - %s\n", spaces, level+1, pMap->dataDesc[i].fieldName, pMap->dataDesc[i].td->dataClassName);
   816 				UTIL_DrawDataTable(fp, pMap->dataDesc[i].td, level+1);
   817 			}
   818 			else
   819 			{
   820 				externalname  = pMap->dataDesc[i].externalName;
   821 				flags = UTIL_DataFlagsToString(pMap->dataDesc[i].flags);
   822 
   823 				if (externalname == NULL)
   824 				{
   825 					fprintf(fp,"%s- %s (%s)(%i Bytes)\n", spaces, pMap->dataDesc[i].fieldName, flags, pMap->dataDesc[i].fieldSizeInBytes);
   826 				}
   827 				else
   828 				{
   829 					fprintf(fp,"%s- %s (%s)(%i Bytes) - %s\n", spaces, pMap->dataDesc[i].fieldName, flags, pMap->dataDesc[i].fieldSizeInBytes, externalname);
   830 				}
   831 			}
   832 		}
   833 		pMap = pMap->baseMap;
   834 	}
   835 }
   836 
   837 CON_COMMAND(sm_dump_datamaps, "Dumps the data map list as a text file")
   838 {
   839 #if SOURCE_ENGINE <= SE_DARKMESSIAH
   840 	CCommand args;
   841 #endif
   842 
   843 	if (args.ArgC() < 2)
   844 	{
   845 		META_CONPRINT("Usage: sm_dump_datamaps <file>\n");
   846 		return;
   847 	}
   848 
   849 	const char *file = args.Arg(1);
   850 	if (!file || file[0] == '\0')
   851 	{
   852 		META_CONPRINT("Usage: sm_dump_datamaps <file>\n");
   853 		return;
   854 	}
   855 
   856 	CEntityFactoryDictionary *dict = GetEntityFactoryDictionary();
   857 	if ( dict == NULL )
   858 	{
   859 		META_CONPRINT("Failed to locate function\n");
   860 		return;
   861 	}
   862 
   863 	char path[PLATFORM_MAX_PATH];
   864 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   865 
   866 	FILE *fp = NULL;
   867 	if ((fp = fopen(path, "wt")) == NULL)
   868 	{
   869 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   870 		return;
   871 	}
   872 
   873 	char buffer[80];
   874 	buffer[0] = 0;
   875 
   876 #if defined SUBPLATFORM_SECURECRT
   877 	_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
   878 #endif
   879 
   880 	time_t t = g_pSM->GetAdjustedTime();
   881 	size_t written = strftime(buffer, sizeof(buffer), "%d/%m/%Y", localtime(&t));
   882 
   883 #if defined SUBPLATFORM_SECURECRT
   884 	_set_invalid_parameter_handler(handler);
   885 #endif
   886 
   887 	fprintf(fp, "// Dump of all datamaps for \"%s\" as at %s\n//\n//\n", g_pSM->GetGameFolderName(), buffer);
   888 
   889 
   890 	fprintf(fp, "// Flag Details:\n//\n");
   891 
   892 	fprintf(fp, "// Global: This field is masked for global entity save/restore\n");
   893 	fprintf(fp, "// Save: This field is saved to disk\n");
   894 	fprintf(fp, "// Key: This field can be requested and written to by string name at load time\n");
   895 	fprintf(fp, "// Input: This field can be written to by string name at run time, and a function called\n");
   896 	fprintf(fp, "// Output: This field propogates it's value to all targets whenever it changes\n");
   897 	fprintf(fp, "// FunctionTable: This is a table entry for a member function pointer\n");
   898 	fprintf(fp, "// Ptr: This field is a pointer, not an embedded object\n");
   899 	fprintf(fp, "// Override: The field is an override for one in a base class (only used by prediction system for now)\n");
   900 
   901 	fprintf(fp, "//\n\n");
   902 
   903 	static int offsEFlags = -1;
   904 	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
   905 	{
   906 		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
   907 		ServerClass *sclass = entity->GetServerClass();
   908 		datamap_t *pMap = gamehelpers->GetDataMap(entity->GetBaseEntity());
   909 
   910 		fprintf(fp,"%s - %s\n", sclass->GetName(), dict->m_Factories.GetElementName(i));
   911 
   912 		UTIL_DrawDataTable(fp, pMap, 0);
   913 
   914 		if (offsEFlags == -1)
   915 		{
   916 			sm_datatable_info_t info;
   917 			if (!gamehelpers->FindDataMapInfo(pMap, "m_iEFlags", &info))
   918 			{
   919 				continue;
   920 			}
   921 
   922 			offsEFlags = info.actual_offset;
   923 		}
   924 		
   925 		int *eflags = (int *)((char *)entity->GetBaseEntity() + offsEFlags);
   926 		*eflags |= (1<<0); // EFL_KILLME
   927 	}
   928 
   929 	fclose(fp);
   930 
   931 }