Skip to content

Commit 5b6250e

Browse files
committed
Added feature to check data signatures at Source.Python setup.
1 parent 0171b36 commit 5b6250e

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

addons/source-python/packages/source-python/__init__.py

100644100755
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def load():
8383
setup_logging()
8484
setup_exception_hooks()
8585
setup_data_update()
86+
setup_check_signatures()
8687
setup_translations()
8788
setup_data()
8889
setup_global_pointers()
@@ -136,6 +137,61 @@ def setup_data_update():
136137
_sp_logger.log_exception(
137138
'An error occured during the data update.', exc_info=True)
138139

140+
def setup_check_signatures():
141+
"""Setup check signatures."""
142+
_sp_logger.log_debug('Checking data signatures...')
143+
144+
# Python Imports
145+
from warnings import warn
146+
# Source.Python Imports
147+
from memory.helpers import check_type_signature_from_file
148+
from memory.helpers import check_pipe_signature_from_file
149+
from paths import SP_DATA_PATH
150+
151+
# iterate over SP_DATA_PATH / 'entities' and extract all files
152+
files = set(SP_DATA_PATH / 'entities' / file.name for file in
153+
(SP_DATA_PATH / 'entities').walkfiles('*.ini'))
154+
155+
# add CBaseClient.
156+
files.add(SP_DATA_PATH / 'client' / 'CBaseClient.ini')
157+
158+
# add CBaseEntityOutput.
159+
files.add(SP_DATA_PATH / 'entity_output' / 'CBaseEntityOutput.ini')
160+
161+
class_warn = False
162+
163+
# check the class type signatures.
164+
for file in files:
165+
for name, identifier in check_type_signature_from_file(file):
166+
if not class_warn:
167+
warn(
168+
'Invalid signature detected in class data, '
169+
'specific function will not work.'
170+
)
171+
class_warn = True
172+
_sp_logger.log_warning(
173+
'Invalid signature detected.\n'
174+
'Name: {0}\nSignature: {1}\nFile: {2}'.format(
175+
name, ' '.join("{:02X}".format(i) for i in identifier), file))
176+
177+
# check the global pointers signature
178+
file = SP_DATA_PATH / 'memory' / 'global_pointers.ini'
179+
180+
gp_warn = False
181+
182+
for name, identifier in check_pipe_signature_from_file(file):
183+
if not gp_warn:
184+
warn(
185+
'Invalid signature detected in global pointers data, '
186+
'may cause problems with Source.Python operation.'
187+
)
188+
gp_warn = True
189+
190+
_sp_logger.log_warning(
191+
'Invalid signature detected.\n'
192+
'Name: {0}\nSignature: {1}\nFile: {2}'.format(
193+
name, ' '.join("{:02X}".format(i) for i in identifier), file))
194+
139195
def setup_data():
140196
"""Setup data."""
141197
_sp_logger.log_debug('Setting up data...')

addons/source-python/packages/source-python/memory/helpers.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111

1212
# Source.Python
1313
# Core
14+
from core import GameConfigObj
1415
from core import PLATFORM
1516
# Memory
1617
from memory import Convention
1718
from memory import DataType
1819
from memory import Function
1920
from memory import Pointer
2021
from memory import TYPE_SIZES
22+
from memory import find_binary
2123
from memory import make_object
2224

2325

@@ -404,6 +406,85 @@ def parse_data(manager, raw_data, keys):
404406

405407
yield (name, temp_data)
406408

409+
def check_type_signature_from_file(file):
410+
"""Checks if the data signatures are valid."""
411+
raw_data = GameConfigObj(file)
412+
413+
# get the binary name from the data(defaults to server if not present)
414+
_binary = Key.as_str(None, raw_data.get(
415+
Key.BINARY + '_' + PLATFORM, raw_data.get(Key.BINARY, 'server')))
416+
_srv_check = Key.as_bool(None, raw_data.get(
417+
Key.SRV_CHECK + '_' + PLATFORM, raw_data.get(Key.SRV_CHECK, 'True')))
418+
419+
key = Key.IDENTIFIER
420+
binary = None
421+
422+
for method, default in (('function', NO_DEFAULT), ):
423+
for name, data in raw_data.get(method, {}).items():
424+
identifier = data.get(key + '_' + PLATFORM, data.get(key, default))
425+
426+
# ignore identifier
427+
if identifier is None:
428+
continue
429+
430+
# if the identifier is NO_DEFAULT, the key is obviously missing
431+
if identifier is NO_DEFAULT:
432+
raise KeyError(
433+
'Missing identifier for "{0}".\nFile: {1}'.format(
434+
name, file))
435+
436+
if binary is None:
437+
try:
438+
binary = find_binary(_binary, _srv_check)
439+
except OSError as error:
440+
print(error)
441+
raise ValueError(
442+
'Could not find the binary.\nFile: {0}'.format(file))
443+
444+
try:
445+
identifier = Key.as_identifier(None, identifier)
446+
binary[identifier]
447+
except ValueError:
448+
yield (name, identifier)
449+
450+
def check_pipe_signature_from_file(file):
451+
"""Checks if the data signatures are valid."""
452+
raw_data = GameConfigObj(file)
453+
454+
key = Key.IDENTIFIER
455+
456+
for name, data in raw_data.items():
457+
identifier = data.get(key + '_' + PLATFORM, data.get(key, NO_DEFAULT))
458+
459+
# if the identifier is NO_DEFAULT, the key is obviously missing
460+
if identifier is NO_DEFAULT:
461+
raise KeyError(
462+
'Missing identifier for "{0}".\nFile: {1}'.format(
463+
name, file))
464+
465+
_binary = Key.as_str(None, data.get(
466+
Key.BINARY + '_' + PLATFORM, data.get(Key.BINARY, NO_DEFAULT)))
467+
if _binary is NO_DEFAULT:
468+
raise KeyError(
469+
'Missing binary for "{0}".\nFile: {1}'.format(
470+
name, file))
471+
472+
_srv_check = Key.as_bool(None, data.get(
473+
Key.SRV_CHECK + '_' + PLATFORM, data.get(Key.SRV_CHECK, 'True')))
474+
475+
try:
476+
binary = find_binary(_binary, _srv_check)
477+
except OSError as error:
478+
print(error)
479+
raise ValueError(
480+
'Could not find the binary.\nFile: {0}'.format(file))
481+
482+
try:
483+
identifier = Key.as_identifier(None, identifier)
484+
binary[identifier]
485+
except ValueError:
486+
yield (name, identifier)
487+
407488
# Use this as a default value if the key is not allowed to have a default
408489
# value
409490
NO_DEFAULT = object()

0 commit comments

Comments
 (0)