diff --git a/addons/source-python/packages/source-python/__init__.py b/addons/source-python/packages/source-python/__init__.py old mode 100644 new mode 100755 index 5ca9588e5..29a1b4739 --- a/addons/source-python/packages/source-python/__init__.py +++ b/addons/source-python/packages/source-python/__init__.py @@ -83,6 +83,7 @@ def load(): setup_logging() setup_exception_hooks() setup_data_update() + setup_check_signatures() setup_translations() setup_data() setup_global_pointers() @@ -136,6 +137,69 @@ def setup_data_update(): _sp_logger.log_exception( 'An error occured during the data update.', exc_info=True) +def setup_check_signatures(): + """Setup check signatures.""" + _sp_logger.log_debug('Checking data signatures...') + + # Python Imports + from warnings import warn + # Source.Python Imports + from memory.helpers import check_type_signature_from_file + from memory.helpers import check_pipe_signature_from_file + from paths import SP_DATA_PATH + + # iterate over SP_DATA_PATH / 'entities' and extract all files + files = set(SP_DATA_PATH / 'entities' / file.name for file in + (SP_DATA_PATH / 'entities').walkfiles('*.ini')) + + # add CBaseClient. + files.add(SP_DATA_PATH / 'client' / 'CBaseClient.ini') + + # add CBaseEntityOutput. + files.add(SP_DATA_PATH / 'entity_output' / 'CBaseEntityOutput.ini') + + class_warn = False + + # check the class type signatures. + for file in files: + for name, identifier in check_type_signature_from_file(file): + if not class_warn: + warn( + 'Invalid signature detected in class data, ' + 'specific function will not work.' + ) + class_warn = True + _sp_logger.log_warning( + 'Invalid signature detected.\n' + 'Name: {0}\nSignature: {1}\nFile: {2}\n'.format( + name, + ' '.join("{:02X}".format(i) for i in identifier), + file + ) + ) + + # check the global pointers signature + file = SP_DATA_PATH / 'memory' / 'global_pointers.ini' + + gp_warn = False + + for name, identifier in check_pipe_signature_from_file(file): + if not gp_warn: + warn( + 'Invalid signature detected in global pointers data, ' + 'may cause problems with Source.Python operation.' + ) + gp_warn = True + + _sp_logger.log_warning( + 'Invalid signature detected.\n' + 'Name: {0}\nSignature: {1}\nFile: {2}\n'.format( + name, + ' '.join("{:02X}".format(i) for i in identifier), + file + ) + ) + def setup_data(): """Setup data.""" _sp_logger.log_debug('Setting up data...') diff --git a/addons/source-python/packages/source-python/memory/helpers.py b/addons/source-python/packages/source-python/memory/helpers.py index 4c0ba2177..c845fb455 100755 --- a/addons/source-python/packages/source-python/memory/helpers.py +++ b/addons/source-python/packages/source-python/memory/helpers.py @@ -11,6 +11,7 @@ # Source.Python # Core +from core import GameConfigObj from core import PLATFORM # Memory from memory import Convention @@ -18,6 +19,7 @@ from memory import Function from memory import Pointer from memory import TYPE_SIZES +from memory import find_binary from memory import make_object @@ -30,6 +32,8 @@ 'MemberFunction', 'NO_DEFAULT', 'Type', + 'check_pipe_signature_from_file', + 'check_type_signature_from_file', 'parse_data', ) @@ -404,6 +408,85 @@ def parse_data(manager, raw_data, keys): yield (name, temp_data) +def check_type_signature_from_file(file): + """Checks if the data signatures are valid.""" + raw_data = GameConfigObj(file) + + # get the binary name from the data(defaults to server if not present) + _binary = Key.as_str(None, raw_data.get( + Key.BINARY + '_' + PLATFORM, raw_data.get(Key.BINARY, 'server'))) + _srv_check = Key.as_bool(None, raw_data.get( + Key.SRV_CHECK + '_' + PLATFORM, raw_data.get(Key.SRV_CHECK, 'True'))) + + key = Key.IDENTIFIER + binary = None + + for method, default in (('function', NO_DEFAULT), ): + for name, data in raw_data.get(method, {}).items(): + identifier = data.get(key + '_' + PLATFORM, data.get(key, default)) + + # ignore identifier + if identifier is None: + continue + + # if the identifier is NO_DEFAULT, the key is obviously missing + if identifier is NO_DEFAULT: + raise KeyError( + 'Missing identifier for "{0}".\nFile: {1}'.format( + name, file)) + + if binary is None: + try: + binary = find_binary(_binary, _srv_check) + except OSError as error: + print(error) + raise ValueError( + 'Could not find the binary.\nFile: {0}'.format(file)) + + try: + identifier = Key.as_identifier(None, identifier) + binary[identifier] + except ValueError: + yield (name, identifier) + +def check_pipe_signature_from_file(file): + """Checks if the data signatures are valid.""" + raw_data = GameConfigObj(file) + + key = Key.IDENTIFIER + + for name, data in raw_data.items(): + identifier = data.get(key + '_' + PLATFORM, data.get(key, NO_DEFAULT)) + + # if the identifier is NO_DEFAULT, the key is obviously missing + if identifier is NO_DEFAULT: + raise KeyError( + 'Missing identifier for "{0}".\nFile: {1}'.format( + name, file)) + + _binary = Key.as_str(None, data.get( + Key.BINARY + '_' + PLATFORM, data.get(Key.BINARY, NO_DEFAULT))) + if _binary is NO_DEFAULT: + raise KeyError( + 'Missing binary for "{0}".\nFile: {1}'.format( + name, file)) + + _srv_check = Key.as_bool(None, data.get( + Key.SRV_CHECK + '_' + PLATFORM, data.get(Key.SRV_CHECK, 'True'))) + + try: + binary = find_binary(_binary, _srv_check) + except OSError as error: + print(error) + raise ValueError( + 'Could not find the binary.\nFile: {0}'.format(file)) + + try: + identifier = Key.as_identifier(None, identifier) + binary[identifier] + except ValueError: + yield (name, identifier) + # Use this as a default value if the key is not allowed to have a default # value NO_DEFAULT = object()