#include "NetVars.h"
namespace NetVars
{
std::vector< CNTClass* > g_Classes;
CNTClass* CNTClass::Analyze( ClientClass *pClass )
{
CNTClass *ret = Analyze( NULL, pClass->m_pRecvTable );
return ret;
}
CNTClass* CNTClass::Analyze( CNTClass *pParent, RecvTable *pTable, int offset, bool addtolist )
{
m_pTable = pTable;
m_nBase = offset;
m_pParent = pParent;
// Check if this table has already been analyzed
CNTClass *current = Find( m_pTable->GetName() );
if ( current ) return current;
// Eventually add to global list
if ( addtolist )
{
m_nPos = g_Classes.size();
AddGlobalClass( this );
}
// Analyze the props
for ( int i = 0; i<m_pTable->GetNumProps(); i++ )
{
CNTVar *var = new CNTVar();
if ( var->Analyze( this, m_pTable->GetPropA(i) ) )
m_Vars.push_back( var );
else
delete var;
}
return NULL;
}
CNTClass* CNTClass::Find( const char *tablename, bool checklist )
{
// Check global list
if ( checklist )
{
CNTClass *top = FindGlobalClass( tablename );
if ( top ) return top;
}
// Check local classes
for ( unsigned int i = 0; i<m_Structs.size(); i++ )
{
if ( !strcmp( tablename, m_Structs[i]->m_pTable->GetName() ) )
return m_Structs[i];
}
// Check parent
if ( m_pParent )
return m_pParent->Find( tablename, false );
// Cannot find class
return NULL;
}
CNTVar* CNTClass::FindVar( const char *varname )
{
for ( unsigned int i = 0; i<m_Vars.size(); i++ )
{
if ( !strcmp( varname, m_Vars[i]->m_pProp->GetName() ) )
return m_Vars[i];
}
return NULL;
}
void CNTClass::ExportCode( std::ostream &out, int depth )
{
// Some basic formatting
std::string pre("");
for ( int i = 0; i<depth; i++ )
pre.append("\t");
// Variables we didn't understand (excludes baseclass and some stuff that is auto discarded)
int failcount = 0;
out << pre << "struct " << GetStructName( !depth );
if ( m_pParent && !depth ) out << " : public " << m_pParent->GetStructName( !depth );
out << "\n" << pre << "{\n";
// Write the subclasses
for ( unsigned int i = 0; i<m_Structs.size(); i++ )
m_Structs[i]->ExportCode( out, depth+1 );
// Write the variables
for ( unsigned int i = 0; i<m_Vars.size(); i++ )
failcount += m_Vars[i]->ExportCode( out, depth+1 )?0:1;
// Write number of failed variables
out << pre << "};";
if ( failcount ) out << " // Fail count = " << failcount;
out << "\n";
}
void CNTClass::ExportRaw( std::ostream &out, int depth )
{
// Some basic formatting
std::string pre("");
for ( int i = 0; i<depth; i++ )
pre.append("\t");
// Name and info
out << pre << "== TABLE : " << m_pTable->GetName() << " ==\n";
for ( unsigned int i = 0; i<m_Structs.size(); i++ )
m_Structs[i]->ExportRaw( out, depth+1 );
out << pre << "-- props : " << m_pTable->GetNumProps() << " --\n";
for ( unsigned int i = 0; i<m_Vars.size(); i++ )
m_Vars[i]->ExportRaw( out, depth+1 );
//out << pre << "== END : " << m_pTable->GetName() << " ==\n";
}
std::string CNTClass::GetStructName( bool forcegendt )
{
std::string str;
if ( m_nClassID>=0 || forcegendt )
{
//str.assign( "Gen" );
str.append( m_pTable->GetName() );
}
else
{
const char *ptr = m_pTable->GetName();
/*if ( strstart(ptr, "m_") ) ptr += 2;
else if ( strstart(ptr, "C") ) ptr += 1;
else if ( strstart(ptr, "DT_") ) ptr += 3;
str.assign( "C" );*/
str.append( ptr );
}
return str;
}
bool CNTVar::Analyze( CNTClass *pClass, RecvProp *pProp )
{
// Store some stuff
m_pProp = pProp;
m_nAbsOffset = pClass->m_nBase + pProp->GetOffset();
m_pParent = pClass;
RecvTable *pSubTable = m_pProp->GetDataTable();
// Check if we are a baseclass
if ( !strcmp( m_pProp->GetName(), "baseclass" ) )
{
// Check if we already analyzed it
CNTClass *old = pClass->Find( pSubTable->GetName() );
if ( !old )
{
// Analyze it
CNTClass *ntclass = new CNTClass();
ntclass->Analyze( (pClass->m_pParent)?pClass->m_pParent->m_pParent:NULL, pSubTable, m_nAbsOffset, true );
// We found our parent
old = ntclass;
}
pClass->m_pParent = old;
// Do not add this to the list of funcs, this is implied through inheritance
return false;
}
// Check if we have a sub table
else if ( pSubTable )
{
int numprops = pSubTable->GetNumProps();
if ( numprops>=1 && !strcmp( pSubTable->GetPropA(0)->GetName(), "000" ) ||
numprops>=2 && !strcmp( pSubTable->GetPropA(1)->GetName(), "000" ) )
{
// This is actually a simple array
m_bBasicType = true;
m_nType = GenType( m_pProp );
m_nArraysize = pSubTable->GetNumProps();
return true;
}
else
{
CNTClass *sub = new CNTClass;
CNTClass *prev = sub->Analyze( pClass, pSubTable, m_nAbsOffset, false );
if ( !prev ) // If it wasn't already analyzed
{
// Add a new struct to our parent
pClass->m_Structs.push_back( sub );
prev = sub;
}
else delete sub;
m_bBasicType = false;
m_pClassType = prev;
return true;
}
}
else
{
// Guess the type of this variable
m_bBasicType = true;
m_nType = GenType( m_pProp );
int arraysize = m_pProp->GetNumElements();
if ( arraysize>1 ) m_nArraysize = arraysize;
return true;
}
}
PropType_t CNTVar::GenType( RecvProp *pProp )
{
PropType_t type = GuessType( pProp->GetName() );
if ( type==PT_NOTYPE || type==PT_NOPREFIX || type==PT_UNKNOWN )
{
switch ( pProp->GetType() )
{
case DPT_Int: return PT_INT;
case DPT_Float: return PT_FLOAT;
case DPT_Vector: return PT_VECTOR;
default: return PT_UNKNOWN;
}
}
return type;
}
PropType_t CNTVar::GuessType( const char *var )
{
if ( strstr(var, "_array_element") ) return PT_ARRAYELEMENT;
else if ( *var=='\"' ) return PT_ARRAYQUOTE;
else if ( !strstart(var, "m_") ) return PT_NOTYPE;
else if ( strstart(var, "m_i") || strstart(var, "m_n") ) return PT_INT;
else if ( strstart(var, "m_fl") ) return PT_FLOAT;
else if ( strstart(var, "m_b") ) return PT_BOOL;
else if ( strstart(var, "m_f") ) return PT_FLAGS;
else if ( strstart(var, "m_ch") ) return PT_CHAR;
else if ( strstart(var, "m_uch") ) return PT_UCHAR;
else if ( strstart(var, "m_h") ) return PT_EHANDLE;
else if ( strstart(var, "m_vec") ) return PT_VECTOR;
else if ( strstart(var, "m_ang") ) return PT_QANGLE;
else if ( strstart(var, "m_psz") || strstart(var, "m_sz") || strstart(var, "m_isz") ) return PT_CHARS;
else if ( var[2]>='A' && var[2]<='Z' ) return PT_NOPREFIX;
else return PT_UNKNOWN;
}
bool CNTVar::ExportCode( std::ostream &out, int depth )
{
const char *pszName = m_pProp->GetName();
// Dirty hack because the source engine likes us
static const char *pszPrevName = NULL;
static bool bCheckit = false;
// Check if we can ignore this one
if ( bCheckit )
{
bCheckit = false;
if ( !strstart( pszPrevName, pszName ) && strlen(pszPrevName)==strlen(pszName)+3 )
return true; // Don't add to failcount
}
// If this contains [0] the next var might be redundant garbage
if ( strstr( pszName, "[0]" ) )
{
bCheckit = true;
pszPrevName = pszName;
}
// Don't make baseclass add to failcount
if ( !strcmp( pszName, "baseclass" ) )
return true;
std::string var, type;
if ( !GetTypeName( type, !depth ) ) return false; // We couldn't figure out what type this is
GetVarName( var );
// Some basic formatting
for ( int i = 0; i<depth; i++ )
out << "\t";
out << "static inline " << type << "* " << var << "(PVOID thisPtr) { return MakePointerGEN< " << type << " >( thisPtr, " << std::hex << "0x" << m_pProp->GetOffset() << " ); }" << std::dec;
if ( m_nArraysize>0 ) out << " // Array size = " << m_nArraysize;
out << "\n";
return true;
}
void CNTVar::ExportRaw( std::ostream &out, int depth )
{
// Some basic formatting
for ( int i = 0; i<depth; i++ )
out << "\t";
// Name and info
std::string type;
RawTypeName( type );
out << "Name = " << m_pProp->GetName() << " \tOffset = " << m_pProp->GetOffset() << " \tType = " << type << "\n";
}
bool CNTVar::GetTypeName( std::string &str, bool parentgendt )
{
if ( m_bBasicType )
{
switch ( m_nType )
{
case PT_CHARS: str.assign( "char*" ); return true;
case PT_CHAR: str.assign( "char" ); return true;
case PT_UCHAR: str.assign( "uchar" ); return true;
case PT_BOOL: str.assign( "bool" ); return true;
case PT_FLOAT: str.assign( "float" ); return true;
case PT_INT: str.assign( "int" ); return true;
case PT_VECTOR: str.assign( "Vector" ); return true;
case PT_QANGLE: str.assign( "QAngle" ); return true;
case PT_EHANDLE: str.assign( "EHANDLE" ); return true;
case PT_FLAGS: str.assign( "int" ); return true;
default: return false;
}
}
else
{
str.assign( m_pClassType->GetStructName( parentgendt ) );
return true;
}
}
void CNTVar::GetVarName( std::string &str )
{
// Shut up
char szName[256];
const char *pszProp = m_pProp->GetName();
/*if ( strstart(pszProp, "m_") ) strcpy_s( szName, pszProp+2 );
else*/ strcpy_s( szName, pszProp );
char *ptr = szName;
for ( ptr = szName; *ptr; ptr++ )
if ( *ptr=='.' ) *ptr = '_';
ptr = strstr( szName, "[" );
if ( ptr )
{
*ptr = *(ptr+1);
*(ptr+1) = 0;
}
str.assign( szName );
}
void CNTVar::RawTypeName( std::string &str )
{
switch ( m_nType )
{
case PT_CHARS: str.assign( "PT_CHARS" ); break;
case PT_CHAR: str.assign( "PT_CHAR" ); break;
case PT_UCHAR: str.assign( "PT_UCHAR" ); break;
case PT_BOOL: str.assign( "PT_BOOL" ); break;
case PT_FLOAT: str.assign( "PT_FLOAT" ); break;
case PT_INT: str.assign( "PT_INT" ); break;
case PT_VECTOR: str.assign( "PT_VECTOR" ); break;
case PT_QANGLE: str.assign( "PT_QANGLE" ); break;
case PT_EHANDLE: str.assign( "PT_EHANDLE" ); break;
case PT_FLAGS: str.assign( "PT_FLAGS" ); break;
case PT_ARRAYELEMENT: str.assign( "PT_ARRAYELEMENT" ); break;
case PT_ARRAYQUOTE: str.assign( "PT_ARRAYQUOTE" ); break;
case PT_NOPREFIX: str.assign( "PT_NOPREFIX" ); break;
case PT_NOTYPE: str.assign( "PT_NOTYPE" ); break;
case PT_UNKNOWN: str.assign( "PT_UNKNOWN" ); break;
default: str.assign( "you broke it" ); break;
}
}
// State
CNTClass *FindGlobalClass( const char *name )
{
for ( unsigned int i = 0; i<g_Classes.size(); i++ )
{
if ( !strcmp( name, g_Classes[i]->m_pTable->GetName() ) )
return g_Classes[i];
}
return NULL;
}
void AddGlobalClass( CNTClass *pClass )
{
// I had more plans with this but that kinda failed
g_Classes.push_back( pClass );
}
void SolveDep( )
{
bool solved = false;
while ( !solved )
{
solved = true;
for ( int i = 0; i<(int)g_Classes.size(); i++ )
{
CNTClass *pParent = g_Classes[i]->m_pParent;
if ( pParent && pParent->m_nPos>i )
{
solved = false;
// Move parent up til right before us
for ( int p = pParent->m_nPos; p>i; p-- )
{
CNTClass *pTemp = g_Classes[p];
g_Classes[p] = g_Classes[p-1];
g_Classes[p-1] = pTemp;
g_Classes[p]->m_nPos++;
g_Classes[p-1]->m_nPos--;
}
}
}
}
}
void Analyze( ClientClass *pClasses )
{
//g_Classes.clear(); // erm this probably doesn't delete all the dynamically allocated CNTClasses...
for ( ; pClasses; pClasses = pClasses->m_pNext )
{
CNTClass *pClass = new CNTClass();
pClass->Analyze( pClasses );
}
}
void GenCode( const char *file )
{
std::ofstream out( file );
out << "#pragma once\n";
out << "#include <windows.h>\n";
out << "template<typename T> inline T* MakePointerGEN( PVOID thisptr, int offset ) { return reinterpret_cast<T*>( reinterpret_cast<UINT_PTR>( thisptr ) + offset ); }\n";
if ( out.bad() || out.fail() )
return;
// Solve dependency problems
SolveDep();
// The data tables
for ( unsigned int i = 0; i<g_Classes.size(); i++ )
g_Classes[i]->ExportCode( out );
// Done :)
out.close();
}
void GenRaw( const char *file )
{
std::ofstream out( file );
if ( out.bad() || out.fail() )
return;
// Start writing
for ( unsigned int i = 0; i<g_Classes.size(); i++ )
g_Classes[i]->ExportRaw( out );
}
};