Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added feature to check data signatures at Source.Python setup. #467

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions addons/source-python/packages/source-python/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def load():
setup_logging()
setup_exception_hooks()
setup_data_update()
setup_check_signatures()
setup_translations()
setup_data()
setup_global_pointers()
Expand Down Expand Up @@ -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...')
Expand Down
83 changes: 83 additions & 0 deletions addons/source-python/packages/source-python/memory/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@

# Source.Python
# Core
from core import GameConfigObj
from core import PLATFORM
# Memory
from memory import Convention
from memory import DataType
from memory import Function
from memory import Pointer
from memory import TYPE_SIZES
from memory import find_binary
from memory import make_object


Expand All @@ -30,6 +32,8 @@
'MemberFunction',
'NO_DEFAULT',
'Type',
'check_pipe_signature_from_file',
'check_type_signature_from_file',
'parse_data',
)

Expand Down Expand Up @@ -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()