extensions/sdktools/vhelpers.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 06:51:21 2008 +0000 (2008-07-11)
branchrefac-jit
changeset 2192 7602d7fb60d9
parent 2168 c24a6fa957db
permissions -rw-r--r--
fixed a compat regression, forgot msvc does funky things with overloaded virtual calls
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2008 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 
    36 CallHelper s_Teleport;
    37 CallHelper s_GetVelocity;
    38 CallHelper s_EyeAngles;
    39 
    40 class CTraceFilterSimple : public CTraceFilterEntitiesOnly
    41 {
    42 public:
    43 	CTraceFilterSimple(const IHandleEntity *passentity): m_pPassEnt(passentity)
    44 	{
    45 	}
    46 	virtual bool ShouldHitEntity(IHandleEntity *pServerEntity, int contentsMask)
    47 	{
    48 		if (pServerEntity == m_pPassEnt)
    49 		{
    50 			return false;
    51 		}
    52 		return true;
    53 	}
    54 private:
    55 	const IHandleEntity *m_pPassEnt;
    56 };
    57 
    58 bool SetupTeleport()
    59 {
    60 	if (s_Teleport.setup)
    61 	{
    62 		return s_Teleport.supported;
    63 	}
    64 
    65 	/* Setup Teleport */
    66 	int offset;
    67 	if (g_pGameConf->GetOffset("Teleport", &offset))
    68 	{
    69 		PassInfo info[3];
    70 		info[0].flags = info[1].flags = info[2].flags = PASSFLAG_BYVAL;
    71 		info[0].size = info[1].size = info[2].size = sizeof(void *);
    72 		info[0].type = info[1].type = info[2].type = PassType_Basic;
    73 
    74 		s_Teleport.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 3);
    75 
    76 		if (s_Teleport.call != NULL)
    77 		{
    78 			s_Teleport.supported = true;
    79 		}
    80 	}
    81 
    82 	s_Teleport.setup = true;
    83 
    84 	return s_Teleport.supported;
    85 }
    86 
    87 void Teleport(CBaseEntity *pEntity, Vector *origin, QAngle *ang, Vector *velocity)
    88 {
    89 	unsigned char params[sizeof(void *) * 4];
    90 	unsigned char *vptr = params;
    91 	*(CBaseEntity **)vptr = pEntity;
    92 	vptr += sizeof(CBaseEntity *);
    93 	*(Vector **)vptr = origin;
    94 	vptr += sizeof(Vector *);
    95 	*(QAngle **)vptr = ang;
    96 	vptr += sizeof(QAngle *);
    97 	*(Vector **)vptr = velocity;
    98 	
    99 	s_Teleport.call->Execute(params, NULL);
   100 }
   101 
   102 bool IsTeleportSupported()
   103 {
   104 	return SetupTeleport();
   105 }
   106 
   107 bool SetupGetVelocity()
   108 {
   109 	if (s_GetVelocity.setup)
   110 	{
   111 		return s_GetVelocity.supported;
   112 	}
   113 
   114 	int offset;
   115 	if (g_pGameConf->GetOffset("GetVelocity", &offset))
   116 	{
   117 		PassInfo info[2];
   118 		info[0].flags = info[1].flags = PASSFLAG_BYVAL;
   119 		info[0].size = info[1].size = sizeof(void *);
   120 		info[0].type = info[1].type = PassType_Basic;
   121 
   122 		s_GetVelocity.call = g_pBinTools->CreateVCall(offset, 0, 0, NULL, info, 2);
   123 
   124 		if (s_GetVelocity.call != NULL)
   125 		{
   126 			s_GetVelocity.supported = true;
   127 		}
   128 	}
   129 
   130 	s_GetVelocity.setup = true;
   131 
   132 	return s_GetVelocity.supported;
   133 }
   134 
   135 void GetVelocity(CBaseEntity *pEntity, Vector *velocity, AngularImpulse *angvelocity)
   136 {
   137 	unsigned char params[sizeof(void *) * 3];
   138 	unsigned char *vptr = params;
   139 	*(CBaseEntity **)vptr = pEntity;
   140 	vptr += sizeof(CBaseEntity *);
   141 	*(Vector **)vptr = velocity;
   142 	vptr += sizeof(Vector *);
   143 	*(AngularImpulse **)vptr = angvelocity;
   144 
   145 	s_GetVelocity.call->Execute(params, NULL);
   146 }
   147 
   148 bool IsGetVelocitySupported()
   149 {
   150 	return SetupGetVelocity();
   151 }
   152 
   153 bool SetupGetEyeAngles()
   154 {
   155 	if (s_EyeAngles.setup)
   156 	{
   157 		return s_EyeAngles.supported;
   158 	}
   159 
   160 	int offset;
   161 	if (g_pGameConf->GetOffset("EyeAngles", &offset))
   162 	{
   163 		PassInfo info[1];
   164 		info[0].flags = PASSFLAG_BYVAL;
   165 		info[0].size = sizeof(void *);
   166 		info[0].type = PassType_Basic;
   167 
   168 		s_EyeAngles.call = g_pBinTools->CreateVCall(offset, 0, 0, info, NULL, 0);
   169 
   170 		if (s_EyeAngles.call != NULL)
   171 		{
   172 			s_EyeAngles.supported = true;
   173 		}
   174 	}
   175 
   176 	s_EyeAngles.setup = true;
   177 
   178 	return s_EyeAngles.supported;
   179 }
   180 
   181 bool GetEyeAngles(CBaseEntity *pEntity, QAngle *pAngles)
   182 {
   183 	if (!IsEyeAnglesSupported())
   184 	{
   185 		return false;
   186 	}
   187 
   188 	QAngle *pRetAngle = NULL;
   189 	unsigned char params[sizeof(void *)];
   190 	unsigned char *vptr = params;
   191 
   192 	*(CBaseEntity **)vptr = pEntity;
   193 
   194 	s_EyeAngles.call->Execute(params, &pRetAngle);
   195 
   196 	if (pRetAngle == NULL)
   197 	{
   198 		return false;
   199 	}
   200 
   201 	*pAngles = *pRetAngle;
   202 
   203 	return true;
   204 }
   205 
   206 int GetClientAimTarget(edict_t *pEdict, bool only_players)
   207 {
   208 	CBaseEntity *pEntity = pEdict->GetUnknown() ? pEdict->GetUnknown()->GetBaseEntity() : NULL;
   209 
   210 	if (pEntity == NULL)
   211 	{
   212 		return -1;
   213 	}
   214 
   215 	Vector eye_position;
   216 	QAngle eye_angles;
   217 
   218 	/* Get the private information we need */
   219 	serverClients->ClientEarPosition(pEdict, &eye_position);
   220 	if (!GetEyeAngles(pEntity, &eye_angles))
   221 	{
   222 		return -2;
   223 	}
   224 
   225 	Vector aim_dir;
   226 	AngleVectors(eye_angles, &aim_dir);
   227 	VectorNormalize(aim_dir);
   228 
   229 	Vector vec_end = eye_position + aim_dir * 8000;
   230 
   231 	Ray_t ray;
   232 	ray.Init(eye_position, vec_end);
   233 
   234 	trace_t tr;
   235 	CTraceFilterSimple simple(pEdict->GetIServerEntity());
   236 
   237 	enginetrace->TraceRay(ray, MASK_SOLID|CONTENTS_DEBRIS|CONTENTS_HITBOX, &simple, &tr);
   238 
   239 	if (tr.fraction == 1.0f || tr.m_pEnt == NULL)
   240 	{
   241 		return -1;
   242 	}
   243 
   244 	edict_t *pTarget = gameents->BaseEntityToEdict(tr.m_pEnt);
   245 	if (pTarget == NULL)
   246 	{
   247 		return -1;
   248 	}
   249 
   250 	int ent_index = engine->IndexOfEdict(pTarget);
   251 
   252 	IGamePlayer *pTargetPlayer = playerhelpers->GetGamePlayer(ent_index);
   253 	if (pTargetPlayer != NULL && !pTargetPlayer->IsInGame())
   254 	{
   255 		return -1;
   256 	}
   257 	else if (only_players && pTargetPlayer == NULL)
   258 	{
   259 		return -1;
   260 	}
   261 
   262 	return ent_index;
   263 }
   264 
   265 bool IsEyeAnglesSupported()
   266 {
   267 	return SetupGetEyeAngles();
   268 }
   269 
   270 bool GetPlayerInfo(int client, player_info_t *info)
   271 {
   272 #if defined ORANGEBOX_BUILD
   273 	return engine->GetPlayerInfo(client, info);
   274 #else
   275 	return (iserver) ? iserver->GetPlayerInfo(client-1, info) : false;
   276 #endif
   277 }
   278 
   279 void ShutdownHelpers()
   280 {
   281 	s_Teleport.Shutdown();
   282 	s_GetVelocity.Shutdown();
   283 	s_EyeAngles.Shutdown();
   284 }
   285 
   286 const char *GetDTTypeName(int type)
   287 {
   288 	switch (type)
   289 	{
   290 	case DPT_Int:
   291 		{
   292 			return "integer";
   293 		}
   294 	case DPT_Float:
   295 		{
   296 			return "float";
   297 		}
   298 	case DPT_Vector:
   299 		{
   300 			return "vector";
   301 		}
   302 	case DPT_String:
   303 		{
   304 			return "string";
   305 		}
   306 	case DPT_Array:
   307 		{
   308 			return "array";
   309 		}
   310 	case DPT_DataTable:
   311 		{
   312 			return "datatable";
   313 		}
   314 	default:
   315 		{
   316 			return NULL;
   317 		}
   318 	}
   319 
   320 	return NULL;
   321 }
   322 
   323 void UTIL_DrawSendTable_XML(FILE *fp, SendTable *pTable, int space_count)
   324 {
   325 	char spaces[255];
   326 
   327 	for (int i = 0; i < space_count; i++)
   328 	{
   329 		spaces[i] = ' ';
   330 	}
   331 	spaces[space_count] = '\0';
   332 
   333 	const char *type_name;
   334 	SendTable *pOtherTable;
   335 	SendProp *pProp;
   336 
   337 	fprintf(fp, " %s<sendtable name=\"%s\">\n", spaces, pTable->GetName());
   338 	for (int i = 0; i < pTable->GetNumProps(); i++)
   339 	{
   340 		pProp = pTable->GetProp(i);
   341 
   342 		fprintf(fp, "  %s<property name=\"%s\">\n", spaces, pProp->GetName());
   343 
   344 		if ((type_name = GetDTTypeName(pProp->GetType())) != NULL)
   345 		{
   346 			fprintf(fp, "   %s<type>%s</type>\n", spaces, type_name);
   347 		}
   348 		else
   349 		{
   350 			fprintf(fp, "   %s<type>%d</type>\n", spaces, pProp->GetType());
   351 		}
   352 
   353 		fprintf(fp, "   %s<offset>%d</offset>\n", spaces, pProp->GetOffset());
   354 		fprintf(fp, "   %s<bits>%d</bits>\n", spaces, pProp->m_nBits);
   355 
   356 		if ((pOtherTable = pTable->GetProp(i)->GetDataTable()) != NULL)
   357 		{
   358 			UTIL_DrawSendTable_XML(fp, pOtherTable, space_count + 3);
   359 		}
   360 
   361 		fprintf(fp, "  %s</property>\n", spaces);
   362 	}
   363 	fprintf(fp, " %s</sendtable>\n", spaces);
   364 }
   365 
   366 void UTIL_DrawServerClass_XML(FILE *fp, ServerClass *sc)
   367 {
   368 	fprintf(fp, "<serverclass name=\"%s\">\n", sc->GetName());
   369 	UTIL_DrawSendTable_XML(fp, sc->m_pTable, 0);
   370 	fprintf(fp, "</serverclass>\n");
   371 }
   372 
   373 void UTIL_DrawSendTable(FILE *fp, SendTable *pTable, int level)
   374 {
   375 	char spaces[255];
   376 	for (int i=0; i<level; i++)
   377 		spaces[i] = ' ';
   378 	spaces[level] = '\0';
   379 
   380 	const char *name, *type;
   381 	SendProp *pProp;
   382 
   383 	fprintf(fp, "%sSub-Class Table (%d Deep): %s\n", spaces, level, pTable->GetName());
   384 
   385 	for (int i=0; i<pTable->GetNumProps(); i++)
   386 	{
   387 		pProp = pTable->GetProp(i);
   388 		name = pProp->GetName();
   389 		if (pProp->GetDataTable())
   390 		{
   391 			UTIL_DrawSendTable(fp, pProp->GetDataTable(), level + 1);
   392 		}
   393 		else
   394 		{
   395 			type = GetDTTypeName(pProp->GetType());
   396 
   397 			if (type != NULL)
   398 			{
   399 				fprintf(fp,
   400 					"%s-Member: %s (offset %d) (type %s) (bits %d)\n", 
   401 					spaces, 
   402 					pProp->GetName(),
   403 					pProp->GetOffset(),
   404 					type,
   405 					pProp->m_nBits);
   406 			}
   407 			else
   408 			{
   409 				fprintf(fp,
   410 					"%s-Member: %s (offset %d) (type %d) (bits %d)\n", 
   411 					spaces, 
   412 					pProp->GetName(),
   413 					pProp->GetOffset(),
   414 					pProp->GetType(),
   415 					pProp->m_nBits);
   416 			}
   417 		}
   418 	}
   419 }
   420 
   421 CON_COMMAND(sm_dump_netprops_xml, "Dumps the networkable property table as an XML file")
   422 {
   423 #if !defined ORANGEBOX_BUILD
   424 	CCommand args;
   425 #endif
   426 
   427 	if (args.ArgC() < 2)
   428 	{
   429 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   430 		return;
   431 	}
   432 
   433 	const char *file = args.Arg(1);
   434 	if (!file || file[0] == '\0')
   435 	{
   436 		META_CONPRINT("Usage: sm_dump_netprops_xml <file>\n");
   437 		return;
   438 	}
   439 
   440 	char path[PLATFORM_MAX_PATH];
   441 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   442 
   443 	FILE *fp = NULL;
   444 	if ((fp = fopen(path, "wt")) == NULL)
   445 	{
   446 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   447 		return;
   448 	}
   449 
   450 	fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
   451 	fprintf(fp, "<!-- Dump of all network properties for \"%s\" follows -->\n\n", g_pSM->GetGameFolderName());
   452 
   453 	ServerClass *pBase = gamedll->GetAllServerClasses();
   454 	while (pBase != NULL)
   455 	{
   456 		UTIL_DrawServerClass_XML(fp, pBase);
   457 		pBase = pBase->m_pNext;
   458 	}
   459 
   460 	fclose(fp);
   461 }
   462 
   463 CON_COMMAND(sm_dump_netprops, "Dumps the networkable property table as a text file")
   464 {
   465 #if !defined ORANGEBOX_BUILD
   466 	CCommand args;
   467 #endif
   468 
   469 	if (args.ArgC() < 2)
   470 	{
   471 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   472 		return;
   473 	}
   474 
   475 	const char *file = args.Arg(1);
   476 	if (!file || file[0] == '\0')
   477 	{
   478 		META_CONPRINT("Usage: sm_dump_netprops <file>\n");
   479 		return;
   480 	}
   481 
   482 	char path[PLATFORM_MAX_PATH];
   483 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   484 
   485 	FILE *fp = NULL;
   486 	if ((fp = fopen(path, "wt")) == NULL)
   487 	{
   488 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   489 		return;
   490 	}
   491 
   492 	fprintf(fp, "// Dump of all network properties for \"%s\" follows\n//\n\n", g_pSM->GetGameFolderName());
   493 
   494 	ServerClass *pBase = gamedll->GetAllServerClasses();
   495 	while (pBase != NULL)
   496 	{
   497 		fprintf(fp, "%s:\n", pBase->GetName());
   498 		UTIL_DrawSendTable(fp, pBase->m_pTable, 1);
   499 		pBase = pBase->m_pNext;
   500 	}
   501 
   502 	fclose(fp);
   503 }
   504 
   505 #if defined SUBPLATFORM_SECURECRT
   506 void _ignore_invalid_parameter(
   507 						const wchar_t * expression,
   508 						const wchar_t * function, 
   509 						const wchar_t * file, 
   510 						unsigned int line,
   511 						uintptr_t pReserved
   512 						)
   513 {
   514 	/* Wow we don't care, thanks Microsoft. */
   515 }
   516 #endif
   517 
   518 CON_COMMAND(sm_dump_classes, "Dumps the class list as a text file")
   519 {
   520 #if !defined ORANGEBOX_BUILD
   521 	CCommand args;
   522 #endif
   523 
   524 	if (args.ArgC() < 2)
   525 	{
   526 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   527 		return;
   528 	}
   529 
   530 	const char *file = args.Arg(1);
   531 	if (!file || file[0] == '\0')
   532 	{
   533 		META_CONPRINT("Usage: sm_dump_classes <file>\n");
   534 		return;
   535 	}
   536 
   537 	ICallWrapper *pWrapper = NULL;
   538 
   539 	if (!pWrapper)
   540 	{
   541 		PassInfo retData;
   542 		retData.flags = PASSFLAG_BYVAL;
   543 		retData.size = sizeof(void *);
   544 		retData.type = PassType_Basic;
   545 
   546 		void *addr;
   547 		if (!g_pGameConf->GetMemSig("EntityFactory", &addr) || addr == NULL)
   548 		{
   549 			META_CONPRINT("Failed to locate function\n");
   550 			return;
   551 		}
   552 
   553 		pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retData, NULL, 0);
   554 	}
   555 
   556 
   557 	void *returnData = NULL;
   558 
   559 	pWrapper->Execute(NULL, &returnData);
   560 
   561 	pWrapper->Destroy();
   562 
   563 	if (returnData == NULL)
   564 	{
   565 		return;
   566 	}
   567 
   568 	CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )returnData;
   569 
   570 	if ( !dict )
   571 	{
   572 		return;
   573 	}
   574 
   575 	char path[PLATFORM_MAX_PATH];
   576 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   577 
   578 	FILE *fp = NULL;
   579 	if ((fp = fopen(path, "wt")) == NULL)
   580 	{
   581 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   582 		return;
   583 	}
   584 
   585 	char buffer[80];
   586 	buffer[0] = 0;
   587 
   588 #if defined SUBPLATFORM_SECURECRT
   589 	_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
   590 #endif
   591 
   592 	time_t t = g_pSM->GetAdjustedTime();
   593 	size_t written = strftime(buffer, sizeof(buffer), "%d/%m/%Y", localtime(&t));
   594 
   595 #if defined SUBPLATFORM_SECURECRT
   596 	_set_invalid_parameter_handler(handler);
   597 #endif
   598 
   599 	fprintf(fp, "// Dump of all classes for \"%s\" as at %s\n//\n\n", g_pSM->GetGameFolderName(), buffer);
   600 
   601 	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
   602 	{
   603 		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
   604 		ServerClass *sclass = entity->GetServerClass();
   605 		fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i));
   606 
   607 		typedescription_t *datamap = gamehelpers->FindInDataMap(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags");
   608 		
   609 		int *eflags = (int *)((char *)entity->GetBaseEntity() + datamap->fieldOffset[TD_OFFSET_NORMAL]);
   610 		*eflags |= (1<<0); // EFL_KILLME
   611 	}
   612 
   613 	fclose(fp);
   614 
   615 }
   616 
   617 char *UTIL_FlagsToString(int flags)
   618 {
   619 	static char str[1024];
   620 	str[0] = 0;
   621 
   622 	if (flags & FTYPEDESC_GLOBAL)
   623 	{
   624 		strcat(str,	"Global|");
   625 	}
   626 	if (flags & FTYPEDESC_SAVE)
   627 	{
   628 		strcat(str,	"Save|");
   629 	}
   630 	if (flags & FTYPEDESC_KEY)
   631 	{
   632 		strcat(str,	"Key|");
   633 	}
   634 	if (flags & FTYPEDESC_INPUT)
   635 	{
   636 		strcat(str,	"Input|");
   637 	}
   638 	if (flags & FTYPEDESC_OUTPUT)
   639 	{
   640 		strcat(str,	"Output|");
   641 	}
   642 	if (flags & FTYPEDESC_FUNCTIONTABLE)
   643 	{
   644 		strcat(str,	"FunctionTable|");
   645 	}
   646 	if (flags & FTYPEDESC_PTR)
   647 	{
   648 		strcat(str,	"Ptr|");
   649 	}
   650 	if (flags & FTYPEDESC_OVERRIDE)
   651 	{
   652 		strcat(str,	"Override|");
   653 	}
   654 
   655 	int len = strlen(str) - 1;
   656 	if (len > 0)
   657 	{
   658 		str[len] = 0; // Strip the final '|'
   659 	}
   660 
   661 	return str;
   662 }
   663 
   664 void UTIL_DrawDataTable(FILE *fp, datamap_t *pMap, int level)
   665 {
   666 	char spaces[255];
   667 
   668 	for (int i=0; i<level; i++)
   669 	{
   670 		spaces[i] = ' ';
   671 	}
   672 
   673 	spaces[level] = '\0';
   674 
   675 	const char *externalname;
   676 	char *flags;
   677 
   678 	while (pMap)
   679 	{
   680 		for (int i=0; i<pMap->dataNumFields; i++)
   681 		{
   682 			if (pMap->dataDesc[i].fieldName == NULL)
   683 			{
   684 				continue;
   685 			}
   686 
   687 			if (pMap->dataDesc[i].td)
   688 			{
   689 				fprintf(fp, " %sSub-Class Table (%d Deep): %s - %s\n", spaces, level+1, pMap->dataDesc[i].fieldName, pMap->dataDesc[i].td->dataClassName);
   690 				UTIL_DrawDataTable(fp, pMap->dataDesc[i].td, level+1);
   691 			}
   692 			else
   693 			{
   694 				externalname  = pMap->dataDesc[i].externalName;
   695 				flags = UTIL_FlagsToString(pMap->dataDesc[i].flags);
   696 
   697 				if (externalname == NULL)
   698 				{
   699 					fprintf(fp,"%s- %s (%s)(%i Bytes)\n", spaces, pMap->dataDesc[i].fieldName, flags, pMap->dataDesc[i].fieldSizeInBytes);
   700 				}
   701 				else
   702 				{
   703 					fprintf(fp,"%s- %s (%s)(%i Bytes) - %s\n", spaces, pMap->dataDesc[i].fieldName, flags, pMap->dataDesc[i].fieldSizeInBytes, externalname);
   704 				}
   705 			}
   706 		}
   707 		pMap = pMap->baseMap;
   708 	}
   709 }
   710 
   711 CON_COMMAND(sm_dump_datamaps, "Dumps the data map list as a text file")
   712 {
   713 #if !defined ORANGEBOX_BUILD
   714 	CCommand args;
   715 #endif
   716 
   717 	if (args.ArgC() < 2)
   718 	{
   719 		META_CONPRINT("Usage: sm_dump_datamaps <file>\n");
   720 		return;
   721 	}
   722 
   723 	const char *file = args.Arg(1);
   724 	if (!file || file[0] == '\0')
   725 	{
   726 		META_CONPRINT("Usage: sm_dump_datamaps <file>\n");
   727 		return;
   728 	}
   729 
   730 	ICallWrapper *pWrapper = NULL;
   731 
   732 	if (!pWrapper)
   733 	{
   734 		PassInfo retData;
   735 		retData.flags = PASSFLAG_BYVAL;
   736 		retData.size = sizeof(void *);
   737 		retData.type = PassType_Basic;
   738 
   739 		void *addr;
   740 		if (!g_pGameConf->GetMemSig("EntityFactory", &addr) || addr == NULL)
   741 		{
   742 			META_CONPRINT("Failed to locate function\n");
   743 			return;
   744 		}
   745 
   746 		pWrapper = g_pBinTools->CreateCall(addr, CallConv_Cdecl, &retData, NULL, 0);
   747 	}
   748 
   749 
   750 	void *returnData = NULL;
   751 
   752 	pWrapper->Execute(NULL, &returnData);
   753 
   754 	pWrapper->Destroy();
   755 
   756 	if (returnData == NULL)
   757 	{
   758 		return;
   759 	}
   760 
   761 	CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )returnData;
   762 
   763 	if ( !dict )
   764 	{
   765 		return;
   766 	}
   767 
   768 	char path[PLATFORM_MAX_PATH];
   769 	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   770 
   771 	FILE *fp = NULL;
   772 	if ((fp = fopen(path, "wt")) == NULL)
   773 	{
   774 		META_CONPRINTF("Could not open file \"%s\"\n", path);
   775 		return;
   776 	}
   777 
   778 	char buffer[80];
   779 	buffer[0] = 0;
   780 
   781 #if defined SUBPLATFORM_SECURECRT
   782 	_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
   783 #endif
   784 
   785 	time_t t = g_pSM->GetAdjustedTime();
   786 	size_t written = strftime(buffer, sizeof(buffer), "%d/%m/%Y", localtime(&t));
   787 
   788 #if defined SUBPLATFORM_SECURECRT
   789 	_set_invalid_parameter_handler(handler);
   790 #endif
   791 
   792 	fprintf(fp, "// Dump of all datamaps for \"%s\" as at %s\n//\n//\n", g_pSM->GetGameFolderName(), buffer);
   793 
   794 
   795 	fprintf(fp, "// Flag Details:\n//\n");
   796 
   797 	fprintf(fp, "// Global: This field is masked for global entity save/restore\n");
   798 	fprintf(fp, "// Save: This field is saved to disk\n");
   799 	fprintf(fp, "// Key: This field can be requested and written to by string name at load time\n");
   800 	fprintf(fp, "// Input: This field can be written to by string name at run time, and a function called\n");
   801 	fprintf(fp, "// Output: This field propogates it's value to all targets whenever it changes\n");
   802 	fprintf(fp, "// FunctionTable: This is a table entry for a member function pointer\n");
   803 	fprintf(fp, "// Ptr: This field is a pointer, not an embedded object\n");
   804 	fprintf(fp, "// Override: The field is an override for one in a base class (only used by prediction system for now)\n");
   805 
   806 	fprintf(fp, "//\n\n");
   807 
   808 	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
   809 	{
   810 		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
   811 		ServerClass *sclass = entity->GetServerClass();
   812 		datamap_t *pMap = gamehelpers->GetDataMap(entity->GetBaseEntity());
   813 
   814 		fprintf(fp,"%s - %s\n", sclass->GetName(), dict->m_Factories.GetElementName(i));
   815 
   816 		UTIL_DrawDataTable(fp, pMap, 0);
   817 
   818 		typedescription_t *datamap = gamehelpers->FindInDataMap(pMap, "m_iEFlags");
   819 		
   820 		int *eflags = (int *)((char *)entity->GetBaseEntity() + datamap->fieldOffset[TD_OFFSET_NORMAL]);
   821 		*eflags |= (1<<0); // EFL_KILLME
   822 	}
   823 
   824 	fclose(fp);
   825 
   826 }