Add support for nested datatables. (bug 5446, r=asherkin)
authorKyle Sanderson <kyle.leet@gmail.com>
Mon Aug 12 00:54:21 2013 +0100 (2013-08-12)
changeset 399759bcfc51eb79
parent 3996 4a0daf289347
child 3998 a40a92c903be
Add support for nested datatables. (bug 5446, r=asherkin)
core/HalfLife2.cpp
core/HalfLife2.h
core/smn_entities.cpp
extensions/sdktools/output.cpp
extensions/sdktools/vhelpers.cpp
extensions/sdktools/vnatives.cpp
plugins/include/entity.inc
public/IGameHelpers.h
     1.1 --- a/core/HalfLife2.cpp	Mon Aug 12 00:45:29 2013 +0100
     1.2 +++ b/core/HalfLife2.cpp	Mon Aug 12 00:54:21 2013 +0100
     1.3 @@ -115,7 +115,7 @@
     1.4  	{
     1.5  		if (h_iter->val.trie)
     1.6  		{
     1.7 -			sm_trie_destroy(h_iter->val.trie);
     1.8 +			delete h_iter->val.trie;
     1.9  			h_iter->val.trie = NULL;
    1.10  		}
    1.11  	}
    1.12 @@ -343,16 +343,11 @@
    1.13  	return false;
    1.14  }
    1.15  
    1.16 -typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name, bool *isNested)
    1.17 +bool UTIL_FindDataMapInfo(datamap_t *pMap, const char *name, sm_datatable_info_t *pDataTable)
    1.18  {
    1.19 -	if (isNested)
    1.20 -	{
    1.21 -		*isNested = false;
    1.22 -	}
    1.23 -	
    1.24  	while (pMap)
    1.25  	{
    1.26 -		for (int i=0; i<pMap->dataNumFields; i++)
    1.27 +		for (int i = 0; i < pMap->dataNumFields; ++i)
    1.28  		{
    1.29  			if (pMap->dataDesc[i].fieldName == NULL)
    1.30  			{
    1.31 @@ -360,32 +355,23 @@
    1.32  			}
    1.33  			if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
    1.34  			{
    1.35 -				return &(pMap->dataDesc[i]);
    1.36 +				pDataTable->prop = &(pMap->dataDesc[i]);
    1.37 +				pDataTable->actual_offset = GetTypeDescOffs(pDataTable->prop);
    1.38 +				return true;
    1.39  			}
    1.40 -			if (pMap->dataDesc[i].td)
    1.41 +			if (pMap->dataDesc[i].td == NULL || !UTIL_FindDataMapInfo(pMap->dataDesc[i].td, name, pDataTable))
    1.42  			{
    1.43 -				if (isNested)
    1.44 -				{
    1.45 -					*isNested = (UTIL_FindInDataMap(pMap->dataDesc[i].td, name, NULL) != NULL);
    1.46 -					if (*isNested)
    1.47 -					{
    1.48 -						return NULL;
    1.49 -					} else {
    1.50 -						continue;
    1.51 -					}
    1.52 -				} else { // Use the old behaviour, we dont want to spring this on extensions - even if they're doing bad things.
    1.53 -					typedescription_t *_td;
    1.54 -					if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name, NULL)) != NULL)
    1.55 -					{
    1.56 -						return _td;
    1.57 -					}
    1.58 -				}
    1.59 +				continue;
    1.60  			}
    1.61 +			
    1.62 +			pDataTable->actual_offset += GetTypeDescOffs(&(pMap->dataDesc[i]));
    1.63 +			return true;
    1.64  		}
    1.65 +		
    1.66  		pMap = pMap->baseMap;
    1.67  	}
    1.68  
    1.69 -	return NULL; 
    1.70 +	return false; 
    1.71  }
    1.72  
    1.73  ServerClass *CHalfLife2::FindServerClass(const char *classname)
    1.74 @@ -472,27 +458,44 @@
    1.75  
    1.76  typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
    1.77  {
    1.78 -	return this->FindInDataMap(pMap, offset, NULL);
    1.79 +	sm_datatable_info_t dt_info;
    1.80 +	
    1.81 +	if (!(this->FindDataMapInfo(pMap, offset, &dt_info)))
    1.82 +	{
    1.83 +		return NULL;
    1.84 +	}
    1.85 +	
    1.86 +	return dt_info.prop;
    1.87  }
    1.88  
    1.89 -typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset, bool *isNested)
    1.90 +bool CHalfLife2::FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable)
    1.91  {
    1.92 -	typedescription_t *td = NULL;
    1.93  	DataMapTrie &val = m_Maps[pMap];
    1.94  
    1.95  	if (!val.trie)
    1.96  	{
    1.97 -		val.trie = sm_trie_create();
    1.98 +		val.trie = new KTrie<sm_datatable_info_t>;
    1.99  	}
   1.100 -	if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
   1.101 +	
   1.102 +	sm_datatable_info_t * pNewTable = val.trie->retrieve(offset);
   1.103 +	
   1.104 +	if (!pNewTable)
   1.105  	{
   1.106 -		if ((td = UTIL_FindInDataMap(pMap, offset, isNested)) != NULL)
   1.107 +		if (UTIL_FindDataMapInfo(pMap, offset, pDataTable))
   1.108  		{
   1.109 -			sm_trie_insert(val.trie, offset, td);
   1.110 +			val.trie->insert(offset, *pDataTable);
   1.111 +		}
   1.112 +		else
   1.113 +		{
   1.114 +			pDataTable->prop = NULL;
   1.115  		}
   1.116  	}
   1.117 +	else
   1.118 +	{
   1.119 +		*pDataTable = *pNewTable;
   1.120 +	}
   1.121  
   1.122 -	return td;
   1.123 +	return (pDataTable->prop != NULL);
   1.124  }
   1.125  
   1.126  void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
   1.127 @@ -1221,8 +1224,14 @@
   1.128  	{
   1.129  		CBaseEntity *pGetterEnt = ReferenceToEntity(0);
   1.130  		datamap_t *pMap = GetDataMap(pGetterEnt);
   1.131 -		typedescription_t *pDesc = FindInDataMap(pMap, "m_iClassname");
   1.132 -		offset = GetTypeDescOffs(pDesc);
   1.133 +		
   1.134 +		sm_datatable_info_t info;
   1.135 +		if (!FindDataMapInfo(pMap, "m_iClassname", &info))
   1.136 +		{
   1.137 +			return NULL;
   1.138 +		}
   1.139 +		
   1.140 +		offset = info.actual_offset;
   1.141  	}
   1.142  
   1.143  	return *(const char **)(((unsigned char *)pEntity) + offset);
     2.1 --- a/core/HalfLife2.h	Mon Aug 12 00:45:29 2013 +0100
     2.2 +++ b/core/HalfLife2.h	Mon Aug 12 00:54:21 2013 +0100
     2.3 @@ -66,7 +66,7 @@
     2.4  struct DataMapTrie
     2.5  {
     2.6  	DataMapTrie() : trie(NULL) {}
     2.7 -	Trie *trie;
     2.8 +	KTrie<sm_datatable_info_t> *trie;
     2.9  };
    2.10  
    2.11  struct DelayedFakeCliCmd
    2.12 @@ -123,7 +123,7 @@
    2.13  	datamap_t *GetDataMap(CBaseEntity *pEntity);
    2.14  	ServerClass *FindServerClass(const char *classname);
    2.15  	typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
    2.16 -	typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset, bool *isNested);
    2.17 +	bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable);
    2.18  	void SetEdictStateChanged(edict_t *pEdict, unsigned short offset);
    2.19  	bool TextMsg(int client, int dest, const char *msg);
    2.20  	bool HintTextMsg(int client, const char *msg);
     3.1 --- a/core/smn_entities.cpp	Mon Aug 12 00:45:29 2013 +0100
     3.2 +++ b/core/smn_entities.cpp	Mon Aug 12 00:54:21 2013 +0100
     3.3 @@ -796,6 +796,82 @@
     3.4  	return info.actual_offset;
     3.5  }
     3.6  
     3.7 +static void GuessDataPropTypes(typedescription_t *td, cell_t * pSize, cell_t * pType)
     3.8 +{
     3.9 +	switch (td->fieldType)
    3.10 +	{
    3.11 +	case FIELD_TICK:
    3.12 +	case FIELD_MODELINDEX:
    3.13 +	case FIELD_MATERIALINDEX:
    3.14 +	case FIELD_INTEGER:
    3.15 +	case FIELD_COLOR32:
    3.16 +		{
    3.17 +			*pType = PropField_Integer;
    3.18 +			*pSize = 32;
    3.19 +			break;
    3.20 +		}
    3.21 +	case FIELD_VECTOR:
    3.22 +	case FIELD_POSITION_VECTOR:
    3.23 +		{
    3.24 +			*pType = PropField_Vector;
    3.25 +			*pSize = 12;
    3.26 +			break;
    3.27 +		}
    3.28 +	case FIELD_SHORT:
    3.29 +		{
    3.30 +			*pType = PropField_Integer;
    3.31 +			*pSize = 16;
    3.32 +			break;
    3.33 +		}
    3.34 +	case FIELD_BOOLEAN:
    3.35 +		{
    3.36 +			*pType = PropField_Integer;
    3.37 +			*pSize = 1;
    3.38 +			break;
    3.39 +		}
    3.40 +	case FIELD_CHARACTER:
    3.41 +		{
    3.42 +			if (td->fieldSize == 1)
    3.43 +			{
    3.44 +				*pType = PropField_Integer;
    3.45 +				*pSize = 8;
    3.46 +			}
    3.47 +			else
    3.48 +			{
    3.49 +				*pType = PropField_String;
    3.50 +				*pSize = 8 * td->fieldSize;
    3.51 +			}
    3.52 +			break;
    3.53 +		}
    3.54 +	case FIELD_MODELNAME:
    3.55 +	case FIELD_SOUNDNAME:
    3.56 +	case FIELD_STRING:
    3.57 +		{
    3.58 +			*pSize = sizeof(string_t);
    3.59 +			*pType = PropField_String_T;
    3.60 +			break;
    3.61 +		}
    3.62 +	case FIELD_FLOAT:
    3.63 +	case FIELD_TIME:
    3.64 +		{
    3.65 +			*pType = PropField_Float;
    3.66 +			*pSize = 32;
    3.67 +			break;
    3.68 +		}
    3.69 +	case FIELD_EHANDLE:
    3.70 +		{
    3.71 +			*pType = PropField_Entity;
    3.72 +			*pSize = 32;
    3.73 +			break;
    3.74 +		}
    3.75 +	default:
    3.76 +		{
    3.77 +			*pType = PropField_Unsupported;
    3.78 +			*pSize = 0;
    3.79 +		}
    3.80 +	}
    3.81 +}
    3.82 +
    3.83  static cell_t FindDataMapOffs(IPluginContext *pContext, const cell_t *params)
    3.84  {
    3.85  	CBaseEntity *pEntity;
    3.86 @@ -815,14 +891,13 @@
    3.87  	}
    3.88  
    3.89  	pContext->LocalToString(params[2], &offset);
    3.90 -	bool isNested = false;
    3.91 -	if ((td=g_HL2.FindInDataMap(pMap, offset, &isNested)) == NULL)
    3.92 +	sm_datatable_info_t info;
    3.93 +	if (!g_HL2.FindDataMapInfo(pMap, offset, &info))
    3.94  	{
    3.95 -		if (isNested)
    3.96 -			return pContext->ThrowNativeError("Property \"%s\" is not safe to access for entity %d", offset, params[1]);
    3.97 -		else
    3.98 -			return -1;
    3.99 +		return -1;
   3.100  	}
   3.101 +	
   3.102 +	td = info.prop;
   3.103  
   3.104  	if (params[0] == 4)
   3.105  	{
   3.106 @@ -830,82 +905,60 @@
   3.107  
   3.108  		pContext->LocalToPhysAddr(params[3], &pType);
   3.109  		pContext->LocalToPhysAddr(params[4], &pSize);
   3.110 +		
   3.111 +		GuessDataPropTypes(td, pSize, pType);
   3.112 +	}
   3.113  
   3.114 -		switch (td->fieldType)
   3.115 +	return GetTypeDescOffs(td);
   3.116 +}
   3.117 +
   3.118 +static cell_t FindDataMapInfo(IPluginContext *pContext, const cell_t *params)
   3.119 +{
   3.120 +	CBaseEntity *pEntity;
   3.121 +	datamap_t *pMap;
   3.122 +	typedescription_t *td;
   3.123 +	char *offset;
   3.124 +	pEntity = GetEntity(params[1]);
   3.125 +
   3.126 +	if (!pEntity)
   3.127 +	{
   3.128 +		return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
   3.129 +	}
   3.130 +
   3.131 +	if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   3.132 +	{
   3.133 +		return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   3.134 +	}
   3.135 +
   3.136 +	pContext->LocalToString(params[2], &offset);
   3.137 +	sm_datatable_info_t info;
   3.138 +	if (!g_HL2.FindDataMapInfo(pMap, offset, &info))
   3.139 +	{
   3.140 +		return -1;
   3.141 +	}
   3.142 +	
   3.143 +	td = info.prop;
   3.144 +
   3.145 +	if (params[0] >= 4)
   3.146 +	{
   3.147 +		cell_t *pType, *pSize;
   3.148 +
   3.149 +		pContext->LocalToPhysAddr(params[3], &pType);
   3.150 +		pContext->LocalToPhysAddr(params[4], &pSize);
   3.151 +		
   3.152 +		GuessDataPropTypes(td, pSize, pType);
   3.153 +		
   3.154 +		if (params[0] == 5)
   3.155  		{
   3.156 -		case FIELD_TICK:
   3.157 -		case FIELD_MODELINDEX:
   3.158 -		case FIELD_MATERIALINDEX:
   3.159 -		case FIELD_INTEGER:
   3.160 -		case FIELD_COLOR32:
   3.161 -			{
   3.162 -				*pType = PropField_Integer;
   3.163 -				*pSize = 32;
   3.164 -				break;
   3.165 -			}
   3.166 -		case FIELD_VECTOR:
   3.167 -		case FIELD_POSITION_VECTOR:
   3.168 -			{
   3.169 -				*pType = PropField_Vector;
   3.170 -				*pSize = 12;
   3.171 -				break;
   3.172 -			}
   3.173 -		case FIELD_SHORT:
   3.174 -			{
   3.175 -				*pType = PropField_Integer;
   3.176 -				*pSize = 16;
   3.177 -				break;
   3.178 -			}
   3.179 -		case FIELD_BOOLEAN:
   3.180 -			{
   3.181 -				*pType = PropField_Integer;
   3.182 -				*pSize = 1;
   3.183 -				break;
   3.184 -			}
   3.185 -		case FIELD_CHARACTER:
   3.186 -			{
   3.187 -				if (td->fieldSize == 1)
   3.188 -				{
   3.189 -					*pType = PropField_Integer;
   3.190 -					*pSize = 8;
   3.191 -				}
   3.192 -				else
   3.193 -				{
   3.194 -					*pType = PropField_String;
   3.195 -					*pSize = 8 * td->fieldSize;
   3.196 -				}
   3.197 -				break;
   3.198 -			}
   3.199 -		case FIELD_MODELNAME:
   3.200 -		case FIELD_SOUNDNAME:
   3.201 -		case FIELD_STRING:
   3.202 -			{
   3.203 -				*pSize = sizeof(string_t);
   3.204 -				*pType = PropField_String_T;
   3.205 -				break;
   3.206 -			}
   3.207 -		case FIELD_FLOAT:
   3.208 -		case FIELD_TIME:
   3.209 -			{
   3.210 -				*pType = PropField_Float;
   3.211 -				*pSize = 32;
   3.212 -				break;
   3.213 -			}
   3.214 -		case FIELD_EHANDLE:
   3.215 -			{
   3.216 -				*pType = PropField_Entity;
   3.217 -				*pSize = 32;
   3.218 -				break;
   3.219 -			}
   3.220 -		default:
   3.221 -			{
   3.222 -				*pType = PropField_Unsupported;
   3.223 -				*pSize = 0;
   3.224 -			}
   3.225 +			cell_t *pLocalOffs;
   3.226 +			
   3.227 +			pContext->LocalToPhysAddr(params[5], &pLocalOffs);
   3.228 +			
   3.229 +			*pLocalOffs = GetTypeDescOffs(info.prop);
   3.230  		}
   3.231  	}
   3.232  
   3.233 -	return GetTypeDescOffs(td);
   3.234 +	return info.actual_offset;
   3.235  }
   3.236  
   3.237  static cell_t GetEntDataString(IPluginContext *pContext, const cell_t *params)
   3.238 @@ -966,23 +1019,16 @@
   3.239  	{ \
   3.240  		return pContext->ThrowNativeError("Could not retrieve datamap"); \
   3.241  	} \
   3.242 -	bool isNested = false; \
   3.243 -	if ((td = g_HL2.FindInDataMap(pMap, prop, &isNested)) == NULL) \
   3.244 +	sm_datatable_info_t info; \
   3.245 +	if (!g_HL2.FindDataMapInfo(pMap, prop, &info)) \
   3.246  	{ \
   3.247  		const char *class_name = g_HL2.GetEntityClassname(pEntity); \
   3.248 -		if (isNested) \
   3.249 -		{ \
   3.250 -			return pContext->ThrowNativeError("Property \"%s\" not safe to access (entity %d/%s)", \
   3.251 -				prop, \
   3.252 -				params[1], \
   3.253 -				((class_name) ? class_name : "")); \
   3.254 -		} else { \
   3.255 -			return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \
   3.256 -				prop, \
   3.257 -				params[1], \
   3.258 -				((class_name) ? class_name : "")); \
   3.259 -		} \
   3.260 -	}
   3.261 +		return pContext->ThrowNativeError("Property \"%s\" not found (entity %d/%s)", \
   3.262 +			prop, \
   3.263 +			params[1], \
   3.264 +			((class_name) ? class_name : "")); \
   3.265 +	} \
   3.266 +	td = info.prop;
   3.267  
   3.268  #define CHECK_SET_PROP_DATA_OFFSET() \
   3.269  	if (element < 0 || element >= td->fieldSize) \
   3.270 @@ -993,7 +1039,7 @@
   3.271  			td->fieldSize); \
   3.272  	} \
   3.273  	\
   3.274 -	offset = GetTypeDescOffs(td) + (element * (td->fieldSizeInBytes / td->fieldSize));
   3.275 +	offset = info.actual_offset + (element * (td->fieldSizeInBytes / td->fieldSize));
   3.276  
   3.277  #define FIND_PROP_SEND(type, type_name) \
   3.278  	sm_sendprop_info_t info;\
   3.279 @@ -1808,7 +1854,7 @@
   3.280  					element);
   3.281  			}
   3.282  
   3.283 -			offset = GetTypeDescOffs(td);
   3.284 +			offset = info.actual_offset;
   3.285  			if (bIsStringIndex)
   3.286  			{
   3.287  				offset += (element * (td->fieldSizeInBytes / td->fieldSize));
   3.288 @@ -1896,25 +1942,23 @@
   3.289  	case Prop_Data:
   3.290  		{
   3.291  			datamap_t *pMap;
   3.292 -			typedescription_t *td;
   3.293  			if ((pMap=CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   3.294  			{
   3.295  				return pContext->ThrowNativeError("Unable to retrieve GetDataDescMap offset");
   3.296  			}
   3.297  			pContext->LocalToString(params[3], &prop);
   3.298 -			bool isNested = false;
   3.299 -			if ((td=g_HL2.FindInDataMap(pMap, prop, &isNested)) == NULL)
   3.300 +			sm_datatable_info_t info;
   3.301 +			if (!g_HL2.FindDataMapInfo(pMap, prop, &info))
   3.302  			{
   3.303 -				if (isNested)
   3.304 -					return pContext->ThrowNativeError("Property \"%s\" is not safe to access for entity %d", prop, params[1]);
   3.305 -				else
   3.306 -					return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   3.307 +				return pContext->ThrowNativeError("Property \"%s\" not found for entity %d", prop, params[1]);
   3.308  			}
   3.309 +			
   3.310 +			typedescription_t *td = info.prop;
   3.311  			if (td->fieldType != FIELD_CHARACTER)
   3.312  			{
   3.313  				return pContext->ThrowNativeError("Property \"%s\" is not a valid string", prop);
   3.314  			}
   3.315 -			offset = GetTypeDescOffs(td);
   3.316 +			offset = info.actual_offset;
   3.317  			maxlen = td->fieldSize;
   3.318  			break;
   3.319  		}
   3.320 @@ -2138,14 +2182,16 @@
   3.321  	{
   3.322  		return pContext->ThrowNativeError("Could not retrieve datamap");
   3.323  	}
   3.324 -	if ((td = g_HL2.FindInDataMap(pMap, prop)) == NULL)
   3.325 +	
   3.326 +	sm_datatable_info_t info;
   3.327 +	if (!g_HL2.FindDataMapInfo(pMap, prop, &info))
   3.328  	{
   3.329  		return pContext->ThrowNativeError("Property \"%s\" not found (entity %d)",
   3.330  			prop,
   3.331  			params[1]);
   3.332  	}
   3.333  
   3.334 -	int offset = GetTypeDescOffs(td);
   3.335 +	int offset = info.actual_offset;
   3.336  
   3.337  	int32_t actual_flags = *(int32_t *)((uint8_t *)pEntity + offset);
   3.338  	int32_t sm_flags = 0;
   3.339 @@ -2176,21 +2222,22 @@
   3.340  		return pContext->ThrowNativeError("Could not find m_fFlags prop in gamedata");
   3.341  	}
   3.342  
   3.343 -	typedescription_t *td;
   3.344  	datamap_t *pMap;
   3.345  
   3.346  	if ((pMap = CBaseEntity_GetDataDescMap(pEntity)) == NULL)
   3.347  	{
   3.348  		return pContext->ThrowNativeError("Could not retrieve datamap");
   3.349  	}
   3.350 -	if ((td = g_HL2.FindInDataMap(pMap, prop)) == NULL)
   3.351 +	
   3.352 +	sm_datatable_info_t info;
   3.353 +	if (!g_HL2.FindDataMapInfo(pMap, prop, &info))
   3.354  	{
   3.355  		return pContext->ThrowNativeError("Property \"%s\" not found (entity %d)",
   3.356  			prop,
   3.357  			params[1]);
   3.358  	}
   3.359  
   3.360 -	int offset = GetTypeDescOffs(td);
   3.361 +	int offset = info.actual_offset;
   3.362  
   3.363  	int32_t sm_flags = params[2];
   3.364  	int32_t actual_flags = 0;
   3.365 @@ -2262,5 +2309,6 @@
   3.366  	{"SetEntPropString",		SetEntPropString},
   3.367  	{"SetEntPropVector",		SetEntPropVector},
   3.368  	{"GetEntityAddress",		GetEntityAddress},
   3.369 +	{"FindDataMapInfo",		FindDataMapInfo},
   3.370  	{NULL,						NULL}
   3.371  };
     4.1 --- a/extensions/sdktools/output.cpp	Mon Aug 12 00:45:29 2013 +0100
     4.2 +++ b/extensions/sdktools/output.cpp	Mon Aug 12 00:54:21 2013 +0100
     4.3 @@ -356,8 +356,13 @@
     4.4  	if (offset == -1)
     4.5  	{
     4.6  		datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
     4.7 -		typedescription_t *pDesc = gamehelpers->FindInDataMap(pMap, "m_iClassname");
     4.8 -		offset = GetTypeDescOffs(pDesc);
     4.9 +		sm_datatable_info_t info;
    4.10 +		if (!gamehelpers->FindDataMapInfo(pMap, "m_iClassname", &info))
    4.11 +		{
    4.12 +			return NULL;
    4.13 +		}
    4.14 +		
    4.15 +		offset = info.actual_offset;
    4.16  	}
    4.17  
    4.18  	return *(const char **)(((unsigned char *)pEntity) + offset);
     5.1 --- a/extensions/sdktools/vhelpers.cpp	Mon Aug 12 00:45:29 2013 +0100
     5.2 +++ b/extensions/sdktools/vhelpers.cpp	Mon Aug 12 00:54:21 2013 +0100
     5.3 @@ -585,14 +585,17 @@
     5.4  
     5.5  	fprintf(fp, "// Dump of all classes for \"%s\" as at %s\n//\n\n", g_pSM->GetGameFolderName(), buffer);
     5.6  
     5.7 +	sm_datatable_info_t info;
     5.8  	for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
     5.9  	{
    5.10  		IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i));
    5.11  		ServerClass *sclass = entity->GetServerClass();
    5.12  		fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i));
    5.13 -
    5.14 -		typedescription_t *datamap = gamehelpers->FindInDataMap(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags");
    5.15 -		int *eflags = (int *)((char *)entity->GetBaseEntity() + GetTypeDescOffs(datamap));
    5.16 +		
    5.17 +		if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags", &info))
    5.18 +			continue;
    5.19 +		
    5.20 +		int *eflags = (int *)((char *)entity->GetBaseEntity() + info.actual_offset);
    5.21  		*eflags |= (1<<0); // EFL_KILLME
    5.22  	}
    5.23  
    5.24 @@ -819,14 +822,13 @@
    5.25  
    5.26  		if (offsEFlags == -1)
    5.27  		{
    5.28 -			typedescription_t *datamap = gamehelpers->FindInDataMap(pMap, "m_iEFlags");
    5.29 -		
    5.30 -			if (!datamap)
    5.31 +			sm_datatable_info_t info;
    5.32 +			if (!gamehelpers->FindDataMapInfo(pMap, "m_iEFlags", &info))
    5.33  			{
    5.34  				continue;
    5.35  			}
    5.36  
    5.37 -			offsEFlags = GetTypeDescOffs(datamap);
    5.38 +			offsEFlags = info.actual_offset;
    5.39  		}
    5.40  		
    5.41  		int *eflags = (int *)((char *)entity->GetBaseEntity() + offsEFlags);
     6.1 --- a/extensions/sdktools/vnatives.cpp	Mon Aug 12 00:45:29 2013 +0100
     6.2 +++ b/extensions/sdktools/vnatives.cpp	Mon Aug 12 00:54:21 2013 +0100
     6.3 @@ -548,10 +548,10 @@
     6.4  		if (frag_prop)
     6.5  		{
     6.6  			datamap_t *pMap = gamehelpers->GetDataMap(pEntity);
     6.7 -			typedescription_t *pType = gamehelpers->FindInDataMap(pMap, frag_prop);
     6.8 -			if (pType != NULL)
     6.9 +			sm_datatable_info_t info;
    6.10 +			if (gamehelpers->FindDataMapInfo(pMap, frag_prop, &info))
    6.11  			{
    6.12 -				s_frag_offs = GetTypeDescOffs(pType);
    6.13 +				s_frag_offs = info.actual_offset;
    6.14  			}
    6.15  		}
    6.16  		if (!s_frag_offs)
    6.17 @@ -684,10 +684,13 @@
    6.18  	static int offset = -1;
    6.19  	if (offset == -1)
    6.20  	{
    6.21 -		offset = GetTypeDescOffs(
    6.22 -			gamehelpers->FindInDataMap(gamehelpers->GetDataMap(pEntity),
    6.23 -			"m_iClassname")
    6.24 -			);
    6.25 +		sm_datatable_info_t info;
    6.26 +		if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(pEntity), "m_iClassname", &info))
    6.27 +		{
    6.28 +			return -1;
    6.29 +		}
    6.30 +		
    6.31 +		offset = info.actual_offset;
    6.32  	}
    6.33  
    6.34  	string_t s;
     7.1 --- a/plugins/include/entity.inc	Mon Aug 12 00:45:29 2013 +0100
     7.2 +++ b/plugins/include/entity.inc	Mon Aug 12 00:54:21 2013 +0100
     7.3 @@ -449,6 +449,26 @@
     7.4  					   const String:prop[],
     7.5  					   &PropFieldType:type=PropFieldType:0,
     7.6  					   &num_bits=0);
     7.7 +					   
     7.8 +/**
     7.9 + * Given an entity, finds a nested datamap property offset.
    7.10 + * This information is cached for future calls.
    7.11 + *
    7.12 + * @param entity		Entity index.
    7.13 + * @param prop			Property name.
    7.14 + * @param type			Optional parameter to store the type.
    7.15 + * @param num_bits		Optional parameter to store the number of bits the field 
    7.16 + *						uses.  The bit count will either be 1 (for boolean) or 
    7.17 + *						divisible by 8 (including 0 if unknown).
    7.18 + * @param local_offset	Optional parameter to store the local offset, as 
    7.19 + *						FindDataMapOffs() would return.
    7.20 + * @return				An offset, or -1 on failure.
    7.21 + */
    7.22 +native FindDataMapInfo(entity, 
    7.23 +					   const String:prop[],
    7.24 +					   &PropFieldType:type=PropFieldType:0,
    7.25 +					   &num_bits=0,
    7.26 +					   &local_offset=0);
    7.27  
    7.28  /**
    7.29   * Wrapper function for finding a send property for a particular entity.
     8.1 --- a/public/IGameHelpers.h	Mon Aug 12 00:45:29 2013 +0100
     8.2 +++ b/public/IGameHelpers.h	Mon Aug 12 00:54:21 2013 +0100
     8.3 @@ -40,7 +40,7 @@
     8.4   */
     8.5  
     8.6  #define SMINTERFACE_GAMEHELPERS_NAME		"IGameHelpers"
     8.7 -#define SMINTERFACE_GAMEHELPERS_VERSION		9
     8.8 +#define SMINTERFACE_GAMEHELPERS_VERSION		10
     8.9  
    8.10  class CBaseEntity;
    8.11  class CBaseHandle;
    8.12 @@ -66,6 +66,12 @@
    8.13  		SendProp *prop;					/**< Property instance. */
    8.14  		unsigned int actual_offset;		/**< Actual computed offset. */
    8.15  	};
    8.16 +	
    8.17 +	struct sm_datatable_info_t
    8.18 +	{
    8.19 +		typedescription_t *prop;			/**< Property instance. */
    8.20 +		unsigned int actual_offset;		/**< Actual computed offset. */
    8.21 +	};
    8.22  
    8.23  	class IGameHelpers : public SMInterface
    8.24  	{
    8.25 @@ -318,6 +324,17 @@
    8.26  		 * @return				True if valid, otherwise false.
    8.27  		 */
    8.28  		virtual bool IsMapValid(const char *map) =0;
    8.29 +		
    8.30 +		/**
    8.31 +		 * @brief Finds a datamap_t definition.
    8.32 +		 *
    8.33 +		 * @param pMap			datamap_t pointer.
    8.34 +		 * @param offset			Property name.
    8.35 +		 * @param pDataTable	Buffer to store sm_datatable_info_t data.
    8.36 +		 * @return				typedescription_t pointer on success, NULL 
    8.37 +		 *						on failure.
    8.38 +		 */
    8.39 +		virtual bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable) =0;
    8.40  	};
    8.41  }
    8.42