|
|
|
@ -17,6 +17,7 @@ GNU General Public License for more details.
@@ -17,6 +17,7 @@ GNU General Public License for more details.
|
|
|
|
|
#include "library.h" |
|
|
|
|
#include "filesystem.h" |
|
|
|
|
#include "server.h" |
|
|
|
|
#include <ctype.h> |
|
|
|
|
|
|
|
|
|
static char s_szLastError[1024] = ""; |
|
|
|
|
|
|
|
|
@ -39,10 +40,35 @@ void COM_PushLibraryError( const char *error )
@@ -39,10 +40,35 @@ void COM_PushLibraryError( const char *error )
|
|
|
|
|
|
|
|
|
|
void *COM_FunctionFromName_SR( void *hInstance, const char *pName ) |
|
|
|
|
{ |
|
|
|
|
char **funcs = NULL; |
|
|
|
|
size_t numfuncs, i; |
|
|
|
|
void *f = NULL; |
|
|
|
|
|
|
|
|
|
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS |
|
|
|
|
if( !memcmp( pName, "ofs:",4 ) ) |
|
|
|
|
return (byte*)svgame.dllFuncs.pfnGameInit + Q_atoi(pName + 4); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#if XASH_MSVC && XASH_X86 |
|
|
|
|
funcs = COM_ConvertToLocalPlatform( MANGLE_VALVE, pName, &numfuncs ); |
|
|
|
|
#elif XASH_POSIX |
|
|
|
|
funcs = COM_ConvertToLocalPlatform( MANGLE_ITANIUM, pName, &numfuncs ); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
if( funcs ) |
|
|
|
|
{ |
|
|
|
|
for( i = 0; i < numfuncs; i++ ) |
|
|
|
|
{ |
|
|
|
|
f = COM_FunctionFromName( hInstance, funcs[i] ); |
|
|
|
|
Z_Free( funcs[i] ); |
|
|
|
|
if( f ) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
Z_Free( funcs ); |
|
|
|
|
|
|
|
|
|
if( f ) return f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return COM_FunctionFromName( hInstance, pName ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -175,3 +201,322 @@ void COM_GetCommonLibraryPath( ECommonLibraryType eLibType, char *out, size_t si
@@ -175,3 +201,322 @@ void COM_GetCommonLibraryPath( ECommonLibraryType eLibType, char *out, size_t si
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
============================================================================= |
|
|
|
|
|
|
|
|
|
C++ MANGLE CONVERSION |
|
|
|
|
|
|
|
|
|
============================================================================= |
|
|
|
|
*/ |
|
|
|
|
#define MAX_NESTED_NAMESPACES 16 /* MSVC limit */ |
|
|
|
|
|
|
|
|
|
static EFunctionMangleType COM_DetectMangleType( const char *str ) |
|
|
|
|
{ |
|
|
|
|
// Itanium C++ ABI mangling always start with _Z
|
|
|
|
|
// namespaces start with N, therefore _ZN
|
|
|
|
|
if( !Q_strncmp( str, "_ZN", 3 ) ) |
|
|
|
|
return MANGLE_ITANIUM; |
|
|
|
|
|
|
|
|
|
// MSVC C++ mangling always start with ? and have
|
|
|
|
|
if( str[0] == '?' && Q_strstr( str, "@@" )) |
|
|
|
|
return MANGLE_MSVC; |
|
|
|
|
|
|
|
|
|
// allow offsets, we just silently ignore them on conversion
|
|
|
|
|
if( !Q_strncmp( str, "ofs:", 4 )) |
|
|
|
|
return MANGLE_OFFSET; |
|
|
|
|
|
|
|
|
|
// don't get confused by MSVC C mangling
|
|
|
|
|
if( str[0] != '@' && Q_strchr( str, '@' )) |
|
|
|
|
return MANGLE_VALVE; |
|
|
|
|
|
|
|
|
|
// not technically an error
|
|
|
|
|
return MANGLE_UNKNOWN; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char *COM_GetMSVCName( const char *in_name ) |
|
|
|
|
{ |
|
|
|
|
static string out_name; |
|
|
|
|
char *pos; |
|
|
|
|
|
|
|
|
|
if( in_name[0] == '?' ) // is this a MSVC C++ mangled name?
|
|
|
|
|
{ |
|
|
|
|
if(( pos = Q_strstr( in_name, "@@" )) != NULL ) |
|
|
|
|
{ |
|
|
|
|
ptrdiff_t len = pos - in_name; |
|
|
|
|
|
|
|
|
|
// strip off the leading '?'
|
|
|
|
|
Q_strncpy( out_name, in_name + 1, sizeof( out_name )); |
|
|
|
|
out_name[len-1] = 0; // terminate string at the "@@"
|
|
|
|
|
return out_name; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Q_strncpy( out_name, in_name, sizeof( out_name )); |
|
|
|
|
|
|
|
|
|
return out_name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char *COM_GetItaniumName( const char * const in_name ) |
|
|
|
|
{ |
|
|
|
|
static string out_name; |
|
|
|
|
const char *f = in_name; |
|
|
|
|
string symbols[16]; |
|
|
|
|
uint len = 0; |
|
|
|
|
int i; |
|
|
|
|
int remaining; |
|
|
|
|
|
|
|
|
|
remaining = Q_strlen( f ); |
|
|
|
|
|
|
|
|
|
if( remaining < 3 ) |
|
|
|
|
goto invalid_format; |
|
|
|
|
|
|
|
|
|
out_name[0] = 0; |
|
|
|
|
|
|
|
|
|
// skip _ZN
|
|
|
|
|
f += 3; |
|
|
|
|
remaining -= 3; |
|
|
|
|
|
|
|
|
|
for( i = 0; i < MAX_NESTED_NAMESPACES; i++ ) |
|
|
|
|
{ |
|
|
|
|
// parse symbol length marker
|
|
|
|
|
len = 0; |
|
|
|
|
for( ; isdigit( *f ) && remaining > 0; f++, remaining-- ) |
|
|
|
|
len = len * 10 + ( *f - '0' ); |
|
|
|
|
|
|
|
|
|
// sane value
|
|
|
|
|
len = min( remaining, len ); |
|
|
|
|
|
|
|
|
|
if( len == 0 ) |
|
|
|
|
goto invalid_format; |
|
|
|
|
|
|
|
|
|
Q_strncpy( symbols[i], f, min( len + 1, sizeof( out_name ))); |
|
|
|
|
f += len; |
|
|
|
|
remaining -= len; |
|
|
|
|
|
|
|
|
|
// end marker
|
|
|
|
|
if( *f == 'E' ) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
if( !isdigit( *f ) || remaining <= 0 ) |
|
|
|
|
goto invalid_format; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( i == MAX_NESTED_NAMESPACES ) |
|
|
|
|
{ |
|
|
|
|
Con_DPrintf( "%s: too much nested namespaces: %s\n", __FUNCTION__, in_name ); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for( ; i >= 0; i-- ) |
|
|
|
|
{ |
|
|
|
|
Q_strncat( out_name, symbols[i], sizeof( out_name )); |
|
|
|
|
if( i > 0 ) |
|
|
|
|
Q_strncat( out_name, "@", sizeof( out_name )); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return out_name; |
|
|
|
|
|
|
|
|
|
invalid_format: |
|
|
|
|
Con_DPrintf( "%s: invalid format: %s\n", __FUNCTION__, in_name ); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char **COM_ConvertToLocalPlatform( EFunctionMangleType to, const char *from, size_t *numfuncs ) |
|
|
|
|
{ |
|
|
|
|
string symbols[MAX_NESTED_NAMESPACES], temp, temp2; |
|
|
|
|
const char *prev; |
|
|
|
|
const char *postfix[3]; |
|
|
|
|
int i = 0; |
|
|
|
|
char **ret; |
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
if( to == MANGLE_MSVC ) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
switch( to ) |
|
|
|
|
{ |
|
|
|
|
case MANGLE_ITANIUM: |
|
|
|
|
postfix[0] = "Ev"; |
|
|
|
|
postfix[1] = "EP11CBaseEntity"; |
|
|
|
|
postfix[2] = "EP11CBaseEntityS1_8USE_TYPEf"; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
ASSERT( 0 ); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
prev = from; |
|
|
|
|
|
|
|
|
|
for( i = 0; i < MAX_NESTED_NAMESPACES; i++ ) |
|
|
|
|
{ |
|
|
|
|
const char *at = Q_strchr( prev, '@' ); |
|
|
|
|
uint len; |
|
|
|
|
|
|
|
|
|
if( at ) len = (uint)( at - prev ); |
|
|
|
|
else len = (uint)Q_strlen( prev ); |
|
|
|
|
Q_strncpy( symbols[i], prev, min( len + 1, sizeof( symbols[i] ))); |
|
|
|
|
prev = at + 1; |
|
|
|
|
|
|
|
|
|
if( !at ) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( i == MAX_NESTED_NAMESPACES ) |
|
|
|
|
{ |
|
|
|
|
Con_DPrintf( "%s: too much nested namespaces: %s\n", __FUNCTION__, from ); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// only three possible variations
|
|
|
|
|
*numfuncs = ARRAYSIZE( postfix ); |
|
|
|
|
ret = Z_Malloc( sizeof( char * ) * ARRAYSIZE( postfix ) ); |
|
|
|
|
|
|
|
|
|
Q_strncpy( temp, "_ZN", sizeof( temp )); |
|
|
|
|
|
|
|
|
|
for( ; i >= 0; i-- ) |
|
|
|
|
{ |
|
|
|
|
Q_snprintf( temp2, sizeof( temp2 ), "%u%s", (uint)Q_strlen( symbols[i] ), symbols[i] ); |
|
|
|
|
Q_strncat( temp, temp2, sizeof( temp )); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( postfix ); i++ ) |
|
|
|
|
{ |
|
|
|
|
Q_snprintf( temp2, sizeof( temp2 ), "%s%s", temp, postfix[i] ); |
|
|
|
|
ret[i] = copystring( temp2 ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *COM_GetPlatformNeutralName( const char *in_name ) |
|
|
|
|
{ |
|
|
|
|
EFunctionMangleType type = COM_DetectMangleType( in_name ); |
|
|
|
|
|
|
|
|
|
switch( type ) |
|
|
|
|
{ |
|
|
|
|
case MANGLE_ITANIUM: return COM_GetItaniumName( in_name ); |
|
|
|
|
case MANGLE_MSVC: return COM_GetMSVCName( in_name ); |
|
|
|
|
default: return in_name; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if XASH_ENGINE_TESTS |
|
|
|
|
#include "tests.h" |
|
|
|
|
|
|
|
|
|
static void Test_DetectMangleType( void ) |
|
|
|
|
{ |
|
|
|
|
TASSERT(COM_DetectMangleType( "asdf" ) == MANGLE_UNKNOWN ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "012345" ) == MANGLE_UNKNOWN ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "?asdf" ) == MANGLE_UNKNOWN ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "_Zasdf" ) == MANGLE_UNKNOWN ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "ofs:1234" ) == MANGLE_OFFSET ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "ofs:asdf" ) == MANGLE_OFFSET ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "ofs:" ) == MANGLE_OFFSET ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "_ZN1f1fEv" ) == MANGLE_ITANIUM ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "_ZN3foo3barEv" ) == MANGLE_ITANIUM ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "?f@f@@msvcsucks" ) == MANGLE_MSVC ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "?foo@bar@@IHATEMSVC" ) == MANGLE_MSVC ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "f@f" ) == MANGLE_VALVE ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "foo@bar" ) == MANGLE_VALVE ); |
|
|
|
|
|
|
|
|
|
// Xash3D FWGS extensions test
|
|
|
|
|
TASSERT(COM_DetectMangleType( "_ZN1f1f1fEv" ) == MANGLE_ITANIUM ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "_ZN3foo3bar3bazEv" ) == MANGLE_ITANIUM ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "?f@f@f@@msvcsucks" ) == MANGLE_MSVC ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "?foo@bar@@IHATEMSVC" ) == MANGLE_MSVC ); |
|
|
|
|
|
|
|
|
|
TASSERT(COM_DetectMangleType( "f@f@f" ) == MANGLE_VALVE ); |
|
|
|
|
TASSERT(COM_DetectMangleType( "foo@bar@baz" ) == MANGLE_VALVE ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void Test_GetMSVCName( void ) |
|
|
|
|
{ |
|
|
|
|
const char *symbols[] = |
|
|
|
|
{ |
|
|
|
|
"", "", |
|
|
|
|
"?f@f@@XYZA", "f@f", |
|
|
|
|
"?foo@bar@@QAEXXZ", "foo@bar", |
|
|
|
|
"foo", "foo", |
|
|
|
|
"?foo", "?foo", |
|
|
|
|
"?foo@@", "foo", // not an error?
|
|
|
|
|
"?foo@bar@baz@@gotstrippedanyway","foo@bar@baz" |
|
|
|
|
}; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( symbols ); i += 2 ) |
|
|
|
|
{ |
|
|
|
|
Msg( "Checking if MSVC '%s' converts to '%s'...\n", symbols[i], symbols[i+1] ); |
|
|
|
|
|
|
|
|
|
TASSERT( !Q_strcmp( COM_GetMSVCName( symbols[i] ), symbols[i+1] )); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void Test_GetItaniumName( void ) |
|
|
|
|
{ |
|
|
|
|
const char *symbols[] = |
|
|
|
|
{ |
|
|
|
|
"", NULL, |
|
|
|
|
"_", NULL, |
|
|
|
|
"_Z", NULL, |
|
|
|
|
"_ZN", NULL, |
|
|
|
|
"_ZNv", NULL, |
|
|
|
|
"_ZN4barr3foo", NULL, |
|
|
|
|
"_ZN3bar3foov", NULL, |
|
|
|
|
"_ZN4bar3fooEv", NULL, |
|
|
|
|
"_ZN3bar3fooEv", "foo@bar", |
|
|
|
|
"_Z3foov", NULL, |
|
|
|
|
"_ZN3fooEv", "foo", // not possible?
|
|
|
|
|
"_ZN3baz3bar3fooEdontcare", "foo@bar@baz", |
|
|
|
|
}; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( symbols ); i += 2 ) |
|
|
|
|
{ |
|
|
|
|
Msg( "Checking if Itanium '%s' converts to '%s'...\n", symbols[i], symbols[i+1] ); |
|
|
|
|
|
|
|
|
|
TASSERT( !Q_strcmp( COM_GetItaniumName( symbols[i] ), symbols[i+1] )); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void Test_ConvertFromValveToLocal( void ) |
|
|
|
|
{ |
|
|
|
|
const char *symbols[] = |
|
|
|
|
{ |
|
|
|
|
"", "_ZN", |
|
|
|
|
"foo", "_ZN3foo", |
|
|
|
|
"xash3d@fwgs", "_ZN4fwgs6xash3d", |
|
|
|
|
"foo@bar@bazz", "_ZN4bazz3bar3foo" |
|
|
|
|
}; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( symbols ); i += 2 ) |
|
|
|
|
{ |
|
|
|
|
char **ret; |
|
|
|
|
size_t numfuncs; |
|
|
|
|
size_t symlen = Q_strlen( symbols[i + 1] ); |
|
|
|
|
|
|
|
|
|
Msg( "Checking if Valve '%s' converts to Itanium '%s'...\n", symbols[i], symbols[i+1] ); |
|
|
|
|
|
|
|
|
|
ret = COM_ConvertToLocalPlatform( MANGLE_ITANIUM, symbols[i], &numfuncs ); |
|
|
|
|
|
|
|
|
|
TASSERT( numfuncs == 3 ); |
|
|
|
|
TASSERT( !Q_strncmp( ret[0], symbols[i+1], symlen )); |
|
|
|
|
TASSERT( !Q_strncmp( ret[1], symbols[i+1], symlen )); |
|
|
|
|
TASSERT( !Q_strncmp( ret[2], symbols[i+1], symlen )); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Test_RunLibCommon( void ) |
|
|
|
|
{ |
|
|
|
|
TRUN( Test_DetectMangleType() ); |
|
|
|
|
TRUN( Test_GetMSVCName() ); |
|
|
|
|
TRUN( Test_GetItaniumName() ); |
|
|
|
|
TRUN( Test_ConvertFromValveToLocal() ); |
|
|
|
|
} |
|
|
|
|
#endif /* XASH_ENGINE_TESTS */ |
|
|
|
|