Merge pull request #15 from Kenzzer/ambuild2.2

Bring build scripts to AMBuild 2.2
This commit is contained in:
sappho 2024-04-19 18:11:27 -04:00 committed by GitHub
commit 7fc53679f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1185 additions and 2707 deletions

165
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,165 @@
name: Extension builder
on:
push:
branches: [ action, master ]
pull_request:
branches: [ action, master ]
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, windows-2019, ubuntu-latest, windows-latest]
include:
- os: ubuntu-20.04
cc: clang-10
cxx: clang++-10
- os: windows-2019
cc: msvc
- os: ubuntu-latest
cc: clang
cxx: clang++
- os: windows-latest
cc: msvc
fail-fast: false
name: ${{ matrix.os }} - ${{ matrix.cc }}
runs-on: ${{ matrix.os }}
env:
PROJECT: 'connect'
SDKS: 'css hl2dm dods tf2'
MMSOURCE_VERSION: '1.10'
SOURCEMOD_VERSION: '1.11'
CACHE_PATH: ${{ github.workspace }}/cache
steps:
- name: Concatenate SDK Names
shell: bash
run: |
# Paranoia
SDKS_VAR="${{env.SDKS}}"
# This will be used in our cache key
echo "SDKS_KEY=${SDKS_VAR//[[:blank:]]/}" >> $GITHUB_ENV
- name: Linux dependencies
if: startsWith(runner.os, 'Linux')
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \
libc6-dev libc6-dev-i386 linux-libc-dev \
linux-libc-dev:i386 lib32z1-dev ${{ matrix.cc }}
- uses: actions/setup-python@v4
name: Setup Python 3.9
with:
python-version: 3.9
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
- uses: actions/checkout@v3
name: Repository checkout
with:
fetch-depth: 0
path: extension
- uses: actions/cache@v3
name: Cache dependencies
env:
cache-name: connect-cache
with:
path: ${{ env.CACHE_PATH }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-sm${{ env.SOURCEMOD_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-${{ env.SDKS_KEY }}
- shell: bash
name: Install dependencies
run: |
mkdir -p "${{ env.CACHE_PATH }}"
cd "${{ env.CACHE_PATH }}"
shallow_checkout () {
# Param 1 is origin
# Param 2 is branch
# Param 3 is name
if [ ! -d "$3" ]; then
git clone "$1" --depth 1 --branch "$2" "$3"
fi
cd "$3"
git remote set-url origin "$1"
git fetch --depth 1 origin "$2"
git checkout --force --recurse-submodules FETCH_HEAD
git submodule init
git submodule update --depth 1
cd ..
}
# We are aware of what we are doing!
git config --global advice.detachedHead false
# Verify github cache, and see if we don't have the sdks already cloned and update them
for sdk in ${{ env.SDKS }}
do
shallow_checkout "https://github.com/alliedmodders/hl2sdk" "${sdk}" "hl2sdk-${sdk}"
done
shallow_checkout "https://github.com/alliedmodders/ambuild" "master" "ambuild"
shallow_checkout "https://github.com/alliedmodders/sourcemod" "${{env.SOURCEMOD_VERSION}}-dev" "sourcemod"
shallow_checkout "https://github.com/alliedmodders/metamod-source/" "${{env.MMSOURCE_VERSION}}-dev" "metamod-source"
# But maybe others aren't (also probably unnecessary because git actions but paranoia)
git config --global advice.detachedHead true
- name: Setup AMBuild
shell: bash
run: |
cd "${{ env.CACHE_PATH }}"
python -m pip install ./ambuild
- name: Select clang compiler
if: startsWith(runner.os, 'Linux')
run: |
echo "CC=${{ matrix.cc }}" >> $GITHUB_ENV
echo "CXX=${{ matrix.cxx }}" >> $GITHUB_ENV
${{ matrix.cc }} --version
${{ matrix.cxx }} --version
- name: Build
shell: bash
working-directory: extension
run: |
mkdir build
cd build
python ../configure.py --enable-auto-versioning --enable-optimize --sdks="${{ env.SDKS }}" --mms-path="${{ env.CACHE_PATH }}/metamod-source" --hl2sdk-root="${{ env.CACHE_PATH }}" --sm-path="${{ env.CACHE_PATH }}/sourcemod"
ambuild
PLATFORM="${{ runner.os }}"
FILENAME="$(cat ./includes/filename_versioning.txt)"
ZIP_FILENAME="${{ env.PROJECT }}-${FILENAME}-${PLATFORM,}.zip"
echo "ZIP_FILENAME=${ZIP_FILENAME}" >> $GITHUB_ENV
- name: Package release - Windows
if: github.event_name == 'push' && github.ref == 'refs/heads/action' && startsWith(matrix.os, 'windows-latest')
working-directory: extension/build/package
run: Compress-Archive -Path * -Destination ${{ env.ZIP_FILENAME }}
- name: Package release
if: github.event_name == 'push' && github.ref == 'refs/heads/action' && startsWith(matrix.os, 'ubuntu-latest')
working-directory: extension/build/package
run: zip -r "${{ env.ZIP_FILENAME }}" .
- name: Upload release
if: github.event_name == 'push' && github.ref == 'refs/heads/action' && (startsWith(matrix.os, 'ubuntu-latest') || startsWith(matrix.os, 'windows-latest'))
shell: bash
working-directory: extension/build/package
run: |
echo "Begin upload..."
AUTHORIZATION="$(echo -n '${{ secrets.USERNAME }}:${{ secrets.PASSWORD }}' | base64)"
echo "::add-mask::${AUTHORIZATION}"
HTTP_CODE=$(curl -XPOST -H "Authorization: Basic ${AUTHORIZATION}" -H "Content-Type: application/zip" --output /dev/null --silent --write-out "%{http_code}" --data-binary "@${{ env.ZIP_FILENAME }}" "https://builds.limetech.io/upload.php?project=${{ env.PROJECT }}&branch=action&filename=${{ env.ZIP_FILENAME }}")
if test ${HTTP_CODE} -ne 200; then
exit ${HTTP_CODE}
fi
echo "Upload successful!"

View File

@ -1,364 +1,434 @@
# vim: set ts=2 sw=2 tw=99 noet ft=python:
import os
import sys
from ambuild.command import Command
from ambuild.command import ShellCommand
from ambuild.command import SymlinkCommand
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os, sys
class ExtractDebugInfoCommand(Command):
def __init__(self, binary, outfile):
Command.__init__(self)
self.binary = binary
self.outfile = outfile
# Simple extensions do not need to modify this file.
def run(self, runner, job):
if not self.binary.NeedsRelink(self.outfile):
return
class SDK(object):
def __init__(self, sdk, ext, aDef, name, platform, dir):
self.folder = 'hl2sdk-' + dir
self.envvar = sdk
self.ext = ext
self.code = aDef
self.define = name
self.platform = platform
self.name = dir
self.path = None # Actual path
self.platformSpec = platform
if AMBuild.target['platform'] == 'linux':
job.AddCommand(ShellCommand('objcopy --only-keep-debug ' + self.outfile + ' ' + self.outfile + '.dbg'))
job.AddCommand(ShellCommand('objcopy --strip-debug ' + self.outfile))
job.AddCommand(ShellCommand('objcopy --add-gnu-debuglink=' + os.path.basename(self.outfile) + '.dbg ' + self.outfile))
elif AMBuild.target['platform'] == 'darwin':
job.AddCommand(ShellCommand('dsymutil ' + self.outfile))
job.AddCommand(ShellCommand('strip -S ' + self.outfile))
# By default, nothing supports x64.
if type(platform) is list:
self.platformSpec = {p: ['x86'] for p in platform}
else:
self.platformSpec = platform
class SM:
def __init__(self):
self.compiler = Cpp.Compiler()
def shouldBuild(self, targets):
for cxx in targets:
if cxx.target.platform in self.platformSpec:
if cxx.target.arch in self.platformSpec[cxx.target.platform]:
return True
return False
#Build SDK info
self.possibleSdks = { }
self.possibleSdks['ep1'] = {'sdk': 'HL2SDK', 'ext': '1.ep1', 'def': '1',
'name': 'EPISODEONE', 'platform': []}
self.possibleSdks['ep2'] = {'sdk': 'HL2SDKOB', 'ext': '2.ep2', 'def': '3',
'name': 'ORANGEBOX', 'platform': []}
self.possibleSdks['css'] = {'sdk': 'HL2SDKCSS', 'ext': '2.css', 'def': '6',
'name': 'CSS', 'platform': []}
self.possibleSdks['ep2v'] = {'sdk': 'HL2SDKOBVALVE', 'ext': '2.ep2v', 'def': '7',
'name': 'ORANGEBOXVALVE', 'platform': ['windows', 'linux', 'darwin']}
self.possibleSdks['l4d'] = {'sdk': 'HL2SDKL4D', 'ext': '2.l4d', 'def': '8',
'name': 'LEFT4DEAD', 'platform': []}
self.possibleSdks['l4d2'] = {'sdk': 'HL2SDKL4D2', 'ext': '2.l4d2', 'def': '9',
'name': 'LEFT4DEAD2', 'platform': []}
self.possibleSdks['darkm'] = {'sdk': 'HL2SDK-DARKM', 'ext': '2.darkm', 'def': '2',
'name': 'DARKMESSIAH', 'platform': []}
self.possibleSdks['swarm'] = {'sdk': 'HL2SDK-SWARM', 'ext': '2.swarm', 'def': '10',
'name': 'ALIENSWARM', 'platform': []}
self.possibleSdks['bgt'] = {'sdk': 'HL2SDK-BGT', 'ext': '2.bgt', 'def': '4',
'name': 'BLOODYGOODTIME', 'platform': []}
self.possibleSdks['eye'] = {'sdk': 'HL2SDK-EYE', 'ext': '2.eye', 'def': '5',
'name': 'EYE', 'platform': []}
self.possibleSdks['csgo'] = {'sdk': 'HL2SDKCSGO', 'ext': '2.csgo', 'def': '12',
'name': 'CSGO', 'platform': []}
WinLinux = ['windows', 'linux']
TF2 = {
'windows': ['x86', 'x86_64'],
'linux': ['x86', 'x86_64']
}
CSGO = {
'windows': ['x86'],
'linux': ['x86', 'x86_64']
}
self.sdkInfo = { }
PossibleSDKs = {
# 'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
# 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'),
# 'orangebox': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'),
# 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'),
# 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinux, 'css'),
'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinux, 'hl2dm'),
'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinux, 'dods'),
'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinux, 'sdk2013'),
# 'bms': SDK('HL2SDKBMS', '2.bms', '11', 'BMS', WinLinux, 'bms'),
'tf2': SDK('HL2SDKTF2', '2.tf2', '12', 'TF2', TF2, 'tf2'),
'l4d': SDK('HL2SDKL4D', '2.l4d', '13', 'LEFT4DEAD', WinLinux, 'l4d'),
# 'nucleardawn': SDK('HL2SDKND', '2.nd', '14', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'),
# 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '15', 'CONTAGION', WinOnly, 'contagion'),
'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '16', 'LEFT4DEAD2', WinLinux, 'l4d2'),
# 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '17', 'ALIENSWARM', WinOnly, 'swarm'),
# 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '18', 'PORTAL2', [], 'portal2'),
# 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
# 'blade': SDK('HL2SDKBLADE', '2.blade', '21', 'BLADE', WinLinux, 'blade'),
'csgo': SDK('HL2SDKCSGO', '2.csgo', '23', 'CSGO', CSGO, 'csgo'),
}
if AMBuild.mode == 'config':
#Detect compilers
self.compiler.DetectAll(AMBuild)
#Detect variables
envvars = { 'MMSOURCE19': 'mmsource-1.9',
'SOURCEMOD14': 'sourcemod-1.7',
'HL2SDKCSS': 'hl2sdk-css',
'HL2SDKOBVALVE': 'hl2sdk-ob-valve',
'HL2SDKL4D': 'hl2sdk-l4d',
'HL2SDKL4D2': 'hl2sdk-l4d2',
'HL2SDKCSGO': 'hl2sdk-csgo'
}
if AMBuild.target['platform'] != 'darwin':
envvars['HL2SDK'] = 'hl2sdk'
envvars['HL2SDKOB'] = 'hl2sdk-ob'
if AMBuild.target['platform'] == 'windows':
envvars['HL2SDK-DARKM'] = 'hl2sdk-darkm'
envvars['HL2SDK-SWARM'] = 'hl2sdk-swarm'
envvars['HL2SDK-BGT'] = 'hl2sdk-bgt'
envvars['HL2SDK-EYE'] = 'hl2sdk-eye'
# Finds if a dict with `key` set to `value` is present on the dict of dicts `dictionary`
def findDictByKey(dictionary, key, value):
for index in dictionary:
elem = dictionary[index]
if elem[key] == value:
return (elem, index)
def ResolveEnvPath(env, folder):
if env in os.environ:
path = os.environ[env]
if os.path.isdir(path):
return path
return None
for i in envvars:
if i in os.environ:
path = os.environ[i]
if not os.path.isdir(path):
raise Exception('Path for {0} was not found: {1}'.format(i, path))
elif i.startswith('HL2SDK'):
(info, sdk) = findDictByKey(self.possibleSdks, 'sdk', i)
self.sdkInfo[sdk] = info
else:
head = os.getcwd()
oldhead = None
while head != None and head != oldhead:
path = os.path.join(head, envvars[i])
path = os.path.join(head, folder)
if os.path.isdir(path):
break
return path
oldhead = head
head, tail = os.path.split(head)
if i.startswith('HL2SDK'):
if head != None and head != oldhead:
(info, sdk) = findDictByKey(self.possibleSdks, 'sdk', i)
self.sdkInfo[sdk] = info
elif head == None or head == oldhead:
raise Exception('Could not find a valid path for {0}'.format(i))
AMBuild.cache.CacheVariable(i, path)
if len(self.sdkInfo) < 1:
raise Exception('At least one SDK must be available.')
return None
AMBuild.cache.CacheVariable('sdkInfo', self.sdkInfo)
def Normalize(path):
return os.path.abspath(os.path.normpath(path))
#Set up defines
cxx = self.compiler.cxx
if isinstance(cxx, Cpp.CompatGCC):
if isinstance(cxx, Cpp.GCC):
self.vendor = 'gcc'
elif isinstance(cxx, Cpp.Clang):
self.vendor = 'clang'
self.compiler.AddToListVar('CDEFINES', 'stricmp=strcasecmp')
self.compiler.AddToListVar('CDEFINES', '_stricmp=strcasecmp')
self.compiler.AddToListVar('CDEFINES', '_snprintf=snprintf')
self.compiler.AddToListVar('CDEFINES', '_vsnprintf=vsnprintf')
self.compiler.AddToListVar('CFLAGS', '-pipe')
self.compiler.AddToListVar('CFLAGS', '-fno-strict-aliasing')
if (self.vendor == 'gcc' and cxx.majorVersion >= 4) or self.vendor == 'clang':
self.compiler.AddToListVar('CFLAGS', '-fvisibility=hidden')
self.compiler.AddToListVar('CXXFLAGS', '-fvisibility-inlines-hidden')
self.compiler.AddToListVar('CFLAGS', '-Wall')
self.compiler.AddToListVar('CFLAGS', '-Werror')
self.compiler.AddToListVar('CFLAGS', '-Wno-uninitialized')
self.compiler.AddToListVar('CFLAGS', '-Wno-unused')
self.compiler.AddToListVar('CFLAGS', '-Wno-switch')
self.compiler.AddToListVar('CFLAGS', '-msse')
self.compiler.AddToListVar('CFLAGS', '-g3')
self.compiler.AddToListVar('CFLAGS', '-m32')
self.compiler.AddToListVar('POSTLINKFLAGS', '-m32')
self.compiler.AddToListVar('CXXFLAGS', '-fno-exceptions')
self.compiler.AddToListVar('CXXFLAGS', '-fno-threadsafe-statics')
self.compiler.AddToListVar('CXXFLAGS', '-Wno-non-virtual-dtor')
self.compiler.AddToListVar('CXXFLAGS', '-Wno-overloaded-virtual')
if (self.vendor == 'gcc' and cxx.majorVersion >= 4 and cxx.minorVersion >= 3) or \
(self.vendor == 'clang' and cxx.majorVersion >= 3):
self.compiler.AddToListVar('CXXFLAGS', '-Wno-delete-non-virtual-dtor')
self.compiler.AddToListVar('CDEFINES', 'HAVE_STDINT_H')
self.compiler.AddToListVar('CDEFINES', 'GNUC')
if self.vendor == 'gcc':
self.compiler.AddToListVar('CFLAGS', '-mfpmath=sse')
elif isinstance(cxx, Cpp.MSVC):
self.vendor = 'msvc'
if AMBuild.options.debug == '1':
self.compiler.AddToListVar('CFLAGS', '/MTd')
self.compiler.AddToListVar('POSTLINKFLAGS', '/NODEFAULTLIB:libcmt')
def SetArchFlags(compiler):
if compiler.behavior == 'gcc':
if compiler.target.arch == 'x86_64':
compiler.cflags += ['-fPIC']
elif compiler.like('msvc'):
if compiler.target.arch == 'x86_64':
compiler.defines += ['WIN64']
class ExtensionConfig(object):
def __init__(self):
self.sdks = {}
self.binaries = []
self.extensions = []
self.generated_headers = None
self.productVersion = None
self.mms_root = None
self.sm_root = None
self.all_targets = []
self.target_archs = set()
if builder.options.targets:
target_archs = builder.options.targets.split(',')
else:
self.compiler.AddToListVar('CFLAGS', '/MT')
self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_DEPRECATE')
self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_WARNINGS')
self.compiler.AddToListVar('CDEFINES', '_CRT_NONSTDC_NO_DEPRECATE')
self.compiler.AddToListVar('CXXFLAGS', '/EHsc')
self.compiler.AddToListVar('CFLAGS', '/W3')
self.compiler.AddToListVar('CFLAGS', '/nologo')
self.compiler.AddToListVar('CFLAGS', '/Zi')
self.compiler.AddToListVar('CXXFLAGS', '/TP')
self.compiler.AddToListVar('POSTLINKFLAGS', '/DEBUG')
self.compiler.AddToListVar('POSTLINKFLAGS', '/MACHINE:X86')
self.compiler.AddToListVar('POSTLINKFLAGS', '/SUBSYSTEM:WINDOWS')
self.compiler.AddToListVar('POSTLINKFLAGS', 'kernel32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'user32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'gdi32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'winspool.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'comdlg32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'advapi32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'shell32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'ole32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'oleaut32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'uuid.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'odbc32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'odbccp32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'legacy_stdio_definitions.lib')
target_archs = ['x86', 'x86_64']
#Optimization
if AMBuild.options.opt == '1':
self.compiler.AddToListVar('CDEFINES', 'NDEBUG')
if self.vendor == 'gcc' or self.vendor == 'clang':
self.compiler.AddToListVar('CFLAGS', '-O3')
elif self.vendor == 'msvc':
self.compiler.AddToListVar('CFLAGS', '/Ox')
self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:ICF')
self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:REF')
for arch in target_archs:
try:
cxx = builder.DetectCxx(target_arch = arch)
self.target_archs.add(cxx.target.arch)
except Exception as e:
# Error if archs were manually overridden.
if builder.options.targets:
raise
print('Skipping target {}: {}'.format(arch, e))
continue
self.all_targets.append(cxx)
#Debugging
if AMBuild.options.debug == '1':
self.compiler.AddToListVar('CDEFINES', 'DEBUG')
self.compiler.AddToListVar('CDEFINES', '_DEBUG')
if self.vendor == 'msvc':
self.compiler.AddToListVar('CFLAGS', '/Od')
self.compiler.AddToListVar('CFLAGS', '/RTC1')
if not self.all_targets:
raise Exception('No suitable C/C++ compiler was found.')
#Platform-specifics
if AMBuild.target['platform'] == 'linux':
self.compiler.AddToListVar('CDEFINES', 'POSIX')
self.compiler.AddToListVar('CDEFINES', '_LINUX')
if self.vendor == 'gcc':
self.compiler.AddToListVar('POSTLINKFLAGS', '-static-libgcc')
if self.vendor == 'clang':
self.compiler.AddToListVar('POSTLINKFLAGS', '-lgcc_eh')
elif AMBuild.target['platform'] == 'darwin':
self.compiler.AddToListVar('CDEFINES', 'OSX')
self.compiler.AddToListVar('CDEFINES', '_OSX')
self.compiler.AddToListVar('POSTLINKFLAGS', '-mmacosx-version-min=10.5')
self.compiler.AddToListVar('POSTLINKFLAGS', ['-arch', 'i386'])
self.compiler.AddToListVar('POSTLINKFLAGS', '-lstdc++')
def use_auto_versioning(self):
return not getattr(builder.options, 'disable_auto_versioning', False)
def AddVersioning(self, binary):
if binary.compiler.target.platform == 'windows':
binary.sources += ['version.rc']
binary.compiler.rcdefines += [
'BINARY_NAME="{0}"'.format(binary.outputFile),
'RC_COMPILE',
]
elif binary.compiler.target.platform == 'mac':
if binary.type == 'library':
binary.compiler.postlink += [
'-compatibility_version', '1.0.0',
'-current_version', self.productVersion
]
if self.use_auto_versioning():
binary.compiler.sourcedeps += self.generated_headers
return binary
@property
def tag(self):
if builder.options.debug == '1':
return 'Debug'
return 'Release'
def detectProductVersion(self):
builder.AddConfigureFile('product.version')
# For OS X dylib versioning
import re
productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
productContents = productFile.read()
productFile.close()
m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents)
with open(os.path.join(builder.sourcePath, 'product.version'), 'r') as fp:
productContents = fp.read()
m = re.match('(\\d+)\.(\\d+)\.(\\d+).*', productContents)
if m == None:
self.version = '1.0.0'
self.productVersion = '1.0.0'
else:
major, minor, release = m.groups()
self.version = '{0}.{1}.{2}'.format(major, minor, release)
AMBuild.cache.CacheVariable('version', self.version)
elif AMBuild.target['platform'] == 'windows':
self.compiler.AddToListVar('CDEFINES', 'WIN32')
self.compiler.AddToListVar('CDEFINES', '_WINDOWS')
self.productVersion = '{0}.{1}.{2}'.format(major, minor, release)
#Finish up
self.compiler.AddToListVar('CDEFINES', 'SOURCEMOD_BUILD')
self.compiler.AddToListVar('CDEFINES', 'SM_GENERATED_BUILD')
self.compiler.AddToListVar('CINCLUDES',
os.path.join(AMBuild.outputFolder, 'includes'))
self.compiler.ToConfig(AMBuild, 'compiler')
AMBuild.cache.CacheVariable('vendor', self.vendor)
self.targetMap = { }
AMBuild.cache.CacheVariable('targetMap', self.targetMap)
def detectSDKs(self):
sdk_list = builder.options.sdks.split(' ')
use_all = sdk_list[0] == 'all'
use_present = sdk_list[0] == 'present'
for sdk_name in PossibleSDKs:
sdk = PossibleSDKs[sdk_name]
if sdk.shouldBuild(self.all_targets):
if builder.options.hl2sdk_root:
sdk_path = os.path.join(os.path.realpath(builder.options.hl2sdk_root), sdk.folder)
else:
self.sdkInfo = AMBuild.cache['sdkInfo']
self.compiler.FromConfig(AMBuild, 'compiler')
self.targetMap = AMBuild.cache['targetMap']
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
if sdk_path is None or not os.path.isdir(sdk_path):
if use_all or sdk_name in sdk_list:
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
continue
if use_all or use_present or sdk_name in sdk_list:
sdk.path = Normalize(sdk_path)
self.sdks[sdk_name] = sdk
if AMBuild.target['platform'] == 'windows':
self.compiler.AddToListVar('RCINCLUDES', os.path.join(AMBuild.sourceFolder, 'extension'))
self.mmsPath = AMBuild.cache['MMSOURCE19']
if len(self.sdks) < 1:
raise Exception('At least one SDK must be available.')
def DefaultCompiler(self):
return self.compiler.Clone()
def JobMatters(self, jobname):
file = sys._getframe().f_code.co_filename
if AMBuild.mode == 'config':
self.targetMap[jobname] = file
return True
if len(AMBuild.args) == 0:
return True
if not jobname in AMBuild.args:
return False
def AutoVersion(self, folder, binary):
if AMBuild.target['platform'] == 'windows':
env = {'RCDEFINES': ['BINARY_NAME="' + binary.binaryFile + '"', 'SM_GENERATED_BUILD']}
binary.AddResourceFile(os.path.join(folder, 'version.rc' ), env)
elif AMBuild.target['platform'] == 'darwin' and isinstance(binary, Cpp.LibraryBuilder):
binary.compiler['POSTLINKFLAGS'].extend(['-compatibility_version', '1.0.0'])
binary.compiler['POSTLINKFLAGS'].extend(['-current_version', AMBuild.cache['version']])
if builder.options.sm_path:
self.sm_root = os.path.realpath(builder.options.sm_path)
else:
return
self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod')
if not self.sm_root:
self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central')
def ExtractDebugInfo(self, job, binary):
src = os.path.join('..', AMBuild.outputFolder, job.workFolder, binary.binaryFile)
job.AddCommand(ExtractDebugInfoCommand(binary, src))
if not self.sm_root or not os.path.isdir(self.sm_root):
raise Exception('Could not find a source copy of SourceMod')
self.sm_root = Normalize(self.sm_root)
def PreSetupHL2Job(self, job, builder, sdk):
info = self.sdkInfo[sdk]
sdkPath = AMBuild.cache[info['sdk']]
if AMBuild.target['platform'] == 'linux':
if sdk == 'ep1':
staticLibs = os.path.join(sdkPath, 'linux_sdk')
if builder.options.mms_path:
self.mms_root = builder.options.mms_path
else:
staticLibs = os.path.join(sdkPath, 'lib', 'linux')
workFolder = os.path.join(AMBuild.outputFolder, job.workFolder)
if sdk in ['ep2v', 'css']:
libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib_srv.so', 'libtier0_srv.so']
for lib in libs:
link = os.path.join(workFolder, lib)
target = os.path.join(staticLibs, lib)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
elif sdk in ['l4d', 'l4d2', 'csgo']:
libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.so', 'libtier0.so']
if sdk == 'csgo':
libs.append('interfaces_i486.a')
for lib in libs:
link = os.path.join(workFolder, lib)
target = os.path.join(staticLibs, lib)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central')
if not self.mms_root or not os.path.isdir(self.mms_root):
raise Exception('Could not find a source copy of Metamod:Source')
self.mms_root = Normalize(self.mms_root)
def configure(self):
if not set(self.target_archs).issubset(['x86', 'x86_64']):
raise Exception('Unknown target architecture: {0}'.format(self.target_archs))
for cxx in self.all_targets:
self.configure_cxx(cxx)
def configure_cxx(self, cxx):
if cxx.family == 'msvc':
if cxx.version < 1900:
raise Exception('Only MSVC 2015 and later are supported, c++14 support is required.')
if cxx.family == 'gcc':
if cxx.version < 'gcc-4.9':
raise Exception('Only GCC versions 4.9 or greater are supported, c++14 support is required.')
if cxx.family == 'clang':
if cxx.version < 'clang-3.4':
raise Exception('Only clang versions 3.4 or greater are supported, c++14 support is required.')
if cxx.like('gcc'):
self.configure_gcc(cxx)
elif cxx.family == 'msvc':
self.configure_msvc(cxx)
cxx.defines += ['HAVE_STRING_H']
# Optimizaiton
if builder.options.opt == '1':
cxx.defines += ['NDEBUG']
# Debugging
if builder.options.debug == '1':
cxx.defines += ['DEBUG', '_DEBUG']
# Platform-specifics
if cxx.target.platform == 'linux':
self.configure_linux(cxx)
elif cxx.target.platform == 'mac':
self.configure_mac(cxx)
elif cxx.target.platform == 'windows':
self.configure_windows(cxx)
cxx.includes += [
os.path.join(self.sm_root, 'public'),
]
if self.use_auto_versioning():
cxx.defines += ['SM_GENERATED_BUILD']
cxx.includes += [
os.path.join(builder.buildPath, 'includes')
]
def configure_gcc(self, cxx):
cxx.defines += [
'stricmp=strcasecmp',
'_stricmp=strcasecmp',
'_snprintf=snprintf',
'_vsnprintf=vsnprintf',
'typeof=__typeof__',
'HAVE_STDINT_H',
'GNUC',
]
cxx.cflags += [
'-pipe',
'-fno-strict-aliasing',
'-Wall',
'-Wno-unused',
'-Wno-switch',
'-Wno-array-bounds',
'-msse',
'-fvisibility=hidden',
]
cxx.cxxflags += [
'-std=c++14',
'-fno-threadsafe-statics',
'-Wno-non-virtual-dtor',
'-Wno-overloaded-virtual',
'-fvisibility-inlines-hidden',
'-fpermissive'
]
have_gcc = cxx.vendor == 'gcc'
have_clang = cxx.vendor == 'clang'
if cxx.version >= 'clang-3.9' or cxx.version == 'clang-3.4' or cxx.version > 'apple-clang-6.0':
cxx.cxxflags += ['-Wno-expansion-to-defined']
if cxx.version == 'clang-3.9' or cxx.version == 'apple-clang-8.0':
cxx.cflags += ['-Wno-varargs']
if cxx.version >= 'clang-3.4' or cxx.version >= 'apple-clang-7.0':
cxx.cxxflags += ['-Wno-inconsistent-missing-override']
if cxx.version >= 'clang-2.9' or cxx.version >= 'apple-clang-3.0':
cxx.cxxflags += ['-Wno-null-dereference']
if have_clang or (cxx.version >= 'gcc-4.6'):
cxx.cflags += ['-Wno-narrowing']
if have_clang or (cxx.version >= 'gcc-4.7'):
cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
if cxx.version >= 'gcc-4.8':
cxx.cflags += ['-Wno-unused-result']
if cxx.version >= 'gcc-9.0':
cxx.cxxflags += ['-Wno-class-memaccess', '-Wno-packed-not-aligned']
if have_clang:
cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch']
if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4':
cxx.cxxflags += ['-Wno-deprecated-register']
else:
libs = ['tier1_i486.a', 'mathlib_i486.a', 'vstdlib_i486.so', 'tier0_i486.so']
for lib in libs:
link = os.path.join(workFolder, lib)
target = os.path.join(staticLibs, lib)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
elif AMBuild.target['platform'] == 'darwin':
staticLibs = os.path.join(sdkPath, 'lib', 'mac')
workFolder = os.path.join(AMBuild.outputFolder, job.workFolder)
libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.dylib', 'libtier0.dylib']
if sdk == 'csgo':
libs.append('interfaces_i486.a')
for lib in libs:
link = os.path.join(workFolder, lib)
target = os.path.join(staticLibs, lib)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
elif AMBuild.target['platform'] == 'windows':
libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
if sdk in ['swarm', 'csgo']:
libs.append('interfaces')
for lib in libs:
libPath = os.path.join(sdkPath, 'lib', 'public', lib) + '.lib'
builder.RebuildIfNewer(libPath)
builder['POSTLINKFLAGS'].append(libPath)
cxx.cxxflags += ['-Wno-deprecated']
cxx.cflags += ['-Wno-sometimes-uninitialized']
def PostSetupHL2Job(self, job, builder, sdk):
if AMBuild.target['platform'] in ['linux', 'darwin']:
builder.AddObjectFiles(['tier1_i486.a', 'mathlib_i486.a'])
if sdk == 'csgo':
builder.AddObjectFiles(['interfaces_i486.a'])
# Work around SDK warnings.
if cxx.version >= 'clang-10.0':
cxx.cflags += ['-Wno-implicit-int-float-conversion', '-Wno-tautological-overlap-compare']
def DefaultHL2Compiler(self, path, sdk, noLink = False, oldMms = '-legacy'):
compiler = self.DefaultCompiler()
if have_gcc:
cxx.cflags += ['-mfpmath=sse']
mms = 'core'
if sdk == 'ep1':
mms += oldMms
if builder.options.opt == '1':
cxx.cflags += ['-O3']
compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms))
compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms, 'sourcehook'))
def configure_msvc(self, cxx):
if builder.options.debug == '1':
cxx.cflags += ['/MTd']
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
else:
cxx.cflags += ['/MT']
cxx.defines += [
'_CRT_SECURE_NO_DEPRECATE',
'_CRT_SECURE_NO_WARNINGS',
'_CRT_NONSTDC_NO_DEPRECATE',
'_ITERATOR_DEBUG_LEVEL=0',
]
cxx.cflags += [
'/W3',
]
cxx.cxxflags += [
'/EHsc',
'/TP',
]
cxx.linkflags += [
'/MACHINE:X86',
'kernel32.lib',
'user32.lib',
'gdi32.lib',
'winspool.lib',
'comdlg32.lib',
'advapi32.lib',
'shell32.lib',
'ole32.lib',
'oleaut32.lib',
'uuid.lib',
'odbc32.lib',
'odbccp32.lib',
]
info = self.possibleSdks
compiler['CDEFINES'].extend(['SE_' + info[i]['name'] + '=' + info[i]['def'] for i in info])
if builder.options.opt == '1':
cxx.cflags += ['/Ox', '/Zo']
cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
paths = [['public'], ['public', 'engine'], ['public', 'mathlib'], ['public', 'vstdlib'], ['public', 'tier0'], ['public', 'tier1']]
if sdk == 'ep1' or sdk == 'darkm':
if builder.options.debug == '1':
cxx.cflags += ['/Od', '/RTC1']
# This needs to be after our optimization flags which could otherwise disable it.
# Don't omit the frame pointer.
cxx.cflags += ['/Oy-']
def configure_linux(self, cxx):
cxx.defines += ['_LINUX', 'POSIX']
cxx.linkflags += ['-Wl,--exclude-libs,ALL', '-lm']
if cxx.vendor == 'gcc':
cxx.linkflags += ['-static-libgcc']
elif cxx.vendor == 'clang':
cxx.linkflags += ['-lgcc_eh']
cxx.linkflags += ['-static-libstdc++']
def configure_mac(self, cxx):
cxx.defines += ['OSX', '_OSX', 'POSIX']
cxx.cflags += ['-mmacosx-version-min=10.7']
cxx.linkflags += [
'-mmacosx-version-min=10.7',
'-arch', 'i386',
'-lstdc++',
'-stdlib=libstdc++',
]
cxx.cxxflags += ['-stdlib=libstdc++']
def configure_windows(self, cxx):
cxx.defines += ['WIN32', '_WINDOWS']
def ConfigureForExtension(self, context, compiler):
compiler.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(context.currentSourcePath, 'sdk'),
os.path.join(self.sm_root, 'public'),
os.path.join(self.sm_root, 'public', 'extensions'),
os.path.join(self.sm_root, 'sourcepawn', 'include'),
os.path.join(self.sm_root, 'public', 'amtl', 'amtl'),
os.path.join(self.sm_root, 'public', 'amtl'),
]
return compiler
def ConfigureForHL2(self, context, binary, sdk):
compiler = binary.compiler
SetArchFlags(compiler)
compiler.cxxincludes += [
os.path.join(self.mms_root, 'core'),
os.path.join(self.mms_root, 'core', 'sourcehook'),
]
defines = ['RAD_TELEMETRY_DISABLED']
defines += ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs]
compiler.defines += defines
paths = [
['public'],
['public', 'engine'],
['public', 'mathlib'],
['public', 'vstdlib'],
['public', 'tier0'],
['public', 'tier1']
]
if sdk.name == 'episode1' or sdk.name == 'darkm':
paths.append(['public', 'dlls'])
paths.append(['game_shared'])
else:
@ -367,59 +437,145 @@ class SM:
paths.append(['game', 'shared'])
paths.append(['common'])
info = self.sdkInfo[sdk]
sdkPath = AMBuild.cache[info['sdk']]
compiler.defines += ['SOURCE_ENGINE=' + sdk.code]
compiler['CDEFINES'].append('SOURCE_ENGINE=' + info['def'])
if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'):
# The 2013 SDK already has these in public/tier0/basetypes.h
compiler.defines.remove('stricmp=strcasecmp')
compiler.defines.remove('_stricmp=strcasecmp')
compiler.defines.remove('_snprintf=snprintf')
compiler.defines.remove('_vsnprintf=vsnprintf')
if sdk in ['swarm','csgo']:
if AMBuild.target['platform'] == 'windows':
compiler['CDEFINES'].extend(['COMPILER_MSVC', 'COMPILER_MSVC32'])
if compiler.like('msvc'):
compiler.defines += ['COMPILER_MSVC']
if compiler.target.arch == 'x86':
compiler.defines += ['COMPILER_MSVC32']
elif compiler.target.arch == 'x86_64':
compiler.defines += ['COMPILER_MSVC64']
compiler.linkflags += ['legacy_stdio_definitions.lib']
else:
compiler['CDEFINES'].extend(['COMPILER_GCC', 'POSIX'])
compiler.defines += ['COMPILER_GCC']
if sdk == 'ep1':
if AMBuild.target['platform'] == 'linux':
staticLibs = os.path.join(sdkPath, 'linux_sdk')
if compiler.target.arch == 'x86_64':
compiler.defines += ['X64BITS', 'PLATFORM_64BITS']
# For everything after Swarm, this needs to be defined for entity networking
# to work properly with sendprop value changes.
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
compiler.defines += ['NETWORK_VARS_ENABLED']
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2']:
if compiler.target.platform in ['linux', 'mac']:
compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE']
if compiler.target.platform == 'linux':
if sdk.name in ['csgo', 'blade']:
compiler.linkflags.remove('-static-libstdc++')
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
if compiler.target.platform == 'linux':
if sdk.name == 'episode1':
lib_folder = os.path.join(sdk.path, 'linux_sdk')
elif sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32')
elif compiler.target.arch == 'x86_64':
lib_folder = os.path.join(sdk.path, 'lib', 'linux64')
else:
if AMBuild.target['platform'] == 'linux':
staticLibs = os.path.join(sdkPath, 'lib', 'linux')
elif AMBuild.target['platform'] == 'darwin':
staticLibs = os.path.join(sdkPath, 'lib', 'mac')
for i in paths:
compiler['CXXINCLUDES'].append(os.path.join(sdkPath, *i))
if not noLink:
if AMBuild.target['platform'] == 'linux':
compiler['POSTLINKFLAGS'][0:0] = ['-lm']
if sdk in ['ep2v', 'css']:
compiler['POSTLINKFLAGS'][0:0] = ['libtier0_srv.so']
compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib_srv.so']
elif sdk in ['l4d', 'l4d2', 'csgo']:
compiler['POSTLINKFLAGS'][0:0] = ['libtier0.so']
compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.so']
lib_folder = os.path.join(sdk.path, 'lib', 'linux')
elif compiler.target.platform == 'mac':
if sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32')
elif compiler.target.arch == 'x86_64':
lib_folder = os.path.join(sdk.path, 'lib', 'osx64')
else:
compiler['POSTLINKFLAGS'][0:0] = ['tier0_i486.so']
compiler['POSTLINKFLAGS'][0:0] = ['vstdlib_i486.so']
elif AMBuild.target['platform'] == 'darwin':
compiler['POSTLINKFLAGS'][0:0] = ['libtier0.dylib']
compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.dylib']
lib_folder = os.path.join(sdk.path, 'lib', 'mac')
return compiler
sm = SM()
globals = {
'SM': sm
}
AMBuild.Include(os.path.join('buildbot', 'Versioning'), globals)
FileList = [
['extension', 'AMBuilder'],
['buildbot', 'PackageScript'],
['buildbot', 'BreakpadSymbols']
if compiler.target.platform in ['linux', 'mac']:
if sdk.name in ['sdk2013', 'bms'] or compiler.target.arch == 'x86_64':
compiler.postlink += [
os.path.join(lib_folder, 'tier1.a'),
os.path.join(lib_folder, 'mathlib.a')
]
else:
compiler.postlink += [
os.path.join(lib_folder, 'tier1_i486.a'),
os.path.join(lib_folder, 'mathlib_i486.a')
]
for parts in FileList:
AMBuild.Include(os.path.join(*parts), globals)
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
if compiler.target.arch == 'x86_64':
compiler.postlink += [os.path.join(lib_folder, 'interfaces.a')]
else:
compiler.postlink += [os.path.join(lib_folder, 'interfaces_i486.a')]
dynamic_libs = []
if compiler.target.platform == 'linux':
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
elif compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'mock']:
dynamic_libs = ['libtier0_client.so', 'libvstdlib_client.so']
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
else:
dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so']
elif compiler.target.platform == 'mac':
compiler.linkflags.append('-liconv')
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
elif compiler.target.platform == 'windows':
libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
if sdk.name in ['swarm', 'blade', 'insurgency', 'doi', 'csgo']:
libs.append('interfaces')
for lib in libs:
if compiler.target.arch == 'x86':
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
elif compiler.target.arch == 'x86_64':
lib_path = os.path.join(sdk.path, 'lib', 'public', 'win64', lib) + '.lib'
compiler.linkflags.append(lib_path)
for library in dynamic_libs:
source_path = os.path.join(lib_folder, library)
output_path = os.path.join(binary.localFolder, library)
# Ensure the output path exists.
context.AddFolder(binary.localFolder)
output = context.AddSymlink(source_path, output_path)
compiler.weaklinkdeps += [output]
compiler.linkflags[0:0] = [library]
return binary
def HL2Config(self, project, context, compiler, name, sdk):
binary = project.Configure(compiler, name,
'{0} - {1} {2}'.format(self.tag, sdk.name, compiler.target.arch))
self.AddVersioning(binary)
return self.ConfigureForHL2(context, binary, sdk)
def HL2ExtConfig(self, project, context, compiler, name, sdk):
binary = project.Configure(compiler, name,
'{0} - {1} {2}'.format(self.tag, sdk.name, compiler.target.arch))
self.AddVersioning(binary)
self.ConfigureForHL2(context, binary, sdk)
self.ConfigureForExtension(context, binary.compiler)
return binary
Extension = ExtensionConfig()
Extension.detectProductVersion()
Extension.detectSDKs()
Extension.configure()
if Extension.use_auto_versioning():
Extension.generated_headers = builder.Build('buildbot/Versioning')
builder.targets = builder.CloneableList(Extension.all_targets)
# Add additional buildscripts here
BuildScripts = [
'extension/AMBuilder',
'buildbot/PackageScript',
'buildbot/BreakpadSymbols'
]
builder.Build(BuildScripts, {'Extension': Extension})

View File

@ -1,41 +1,39 @@
# vim: set ts=2 sw=2 tw=99 noet ft=python:
import os
import urllib.request
from ambuild.command import Command
from ambuild.command import ShellCommand
import os, sys
class IterateDebugInfoCommand(Command):
def run(self, master, job):
pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt')
for debug_info in pdblog:
debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip())
job.AddCommand(SymbolCommand(debug_info, symbolServer))
pdblog.close()
class SymbolCommand(ShellCommand):
def __init__(self, debugFile, symbolServer):
self.serverResponse = None
self.symbolServer = symbolServer
if AMBuild.target['platform'] == 'linux':
cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile))
elif AMBuild.target['platform'] == 'darwin':
cmdstring = "dump_syms {0}".format(debugFile)
elif AMBuild.target['platform'] == 'windows':
cmdstring = "dump_syms.exe {0}".format(debugFile)
ShellCommand.__init__(self, cmdstring)
def run(self, master, job):
ShellCommand.run(self, master, job)
if self.stdout != None and len(self.stdout) > 0:
request = urllib.request.Request(symbolServer, self.stdout.encode('utf-8'))
request.add_header("Content-Type", "text/plain")
self.serverResponse = urllib.request.urlopen(request).read().decode('utf-8')
def spew(self, runner):
if self.stderr != None and len(self.stderr) > 0:
runner.PrintOut(self.stderr)
if self.serverResponse != None and len(self.serverResponse) > 0:
runner.PrintOut(self.serverResponse)
UPLOAD_SCRIPT = os.path.join(Extension.sm_root, 'tools', 'buildbot', 'upload_symbols.py')
if 'BREAKPAD_SYMBOL_SERVER' in os.environ:
symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER']
job = AMBuild.AddJob('breakpad-symbols')
job.AddCommand(IterateDebugInfoCommand())
builder.SetBuildFolder('breakpad-symbols')
for cxx_task in Extension.extensions:
if cxx_task.target.platform in ['windows']:
debug_entry = cxx_task.debug
else:
debug_entry = cxx_task.binary
debug_file = os.path.join(builder.buildPath, debug_entry.path)
if cxx_task.target.platform == 'linux':
argv = ['dump_syms', debug_file, os.path.dirname(debug_file)]
elif cxx_task.target.platform == 'mac':
# Required once dump_syms is updated on the slaves.
#argv = ['dump_syms', '-g', debug_file + '.dSYM', debug_file]
argv = ['dump_syms', debug_file + '.dSYM']
elif cxx_task.target.platform == 'windows':
argv = ['dump_syms.exe', debug_file]
plat_dir = os.path.dirname(debug_file)
bin_dir = os.path.split(plat_dir)[0]
symbol_file = '{}-{}-{}.breakpad'.format(
os.path.split(bin_dir)[1],
cxx_task.target.platform,
cxx_task.target.arch)
argv = [sys.executable, UPLOAD_SCRIPT, symbol_file] + argv
builder.AddCommand(
inputs = [UPLOAD_SCRIPT, debug_entry],
argv = argv,
outputs = [symbol_file]
)

View File

@ -4,112 +4,44 @@ import shutil
import ambuild.osutil as osutil
from ambuild.command import Command
job = AMBuild.AddJob('package')
builder.SetBuildFolder('package')
class DestroyPath(Command):
def __init__(self, folder):
Command.__init__(self)
self.folder = folder
def CreateFolders(folders):
dict = {}
for folder in folders:
path = os.path.normpath(folder)
dict[folder] = builder.AddFolder(path)
return dict
def destroy(self, path):
entries = os.listdir(path)
for entry in entries:
newpath = os.path.join(path, entry)
if os.path.isdir(newpath):
self.destroy(newpath)
os.rmdir(newpath)
elif os.path.isfile(newpath):
os.remove(newpath)
def run(self, runner, job):
runner.PrintOut('rm -rf {0}/*'.format(self.folder))
self.destroy(self.folder)
class CreateFolders(Command):
def __init__(self, folders):
Command.__init__(self)
self.folders = folders
def run(self, runner, job):
for folder in self.folders:
path = os.path.join(*folder)
runner.PrintOut('mkdir {0}'.format(path))
os.makedirs(path)
#Shallow folder copy
class CopyFolder(Command):
def __init__(self, fromList, toList, excludes = []):
Command.__init__(self)
self.fromPath = os.path.join(AMBuild.sourceFolder, *fromList)
self.toPath = os.path.join(*toList)
self.excludes = excludes
def run(self, runner, job):
entries = os.listdir(self.fromPath)
for entry in entries:
if entry in self.excludes:
def CopyFiles(src, dest, filter_ext=None):
source_path = os.path.join(builder.sourcePath, src)
if os.path.isfile(source_path):
builder.AddCopy(source_path, dest)
return
for entry in os.listdir(source_path):
entry_path = os.path.join(source_path, entry)
if not os.path.isfile(entry_path):
continue
path = os.path.join(self.fromPath, entry)
if not os.path.isfile(path):
if filter_ext:
_, ext = os.path.splitext(entry)
if filter_ext != ext:
continue
runner.PrintOut('copy {0} to {1}'.format(path, self.toPath))
shutil.copy(path, self.toPath)
#Single file copy
class CopyFile(Command):
def __init__(self, fromFile, toPath):
Command.__init__(self)
self.fromFile = fromFile
self.toPath = toPath
def run(self, runner, job):
runner.PrintOut('copy {0} to {1}'.format(self.fromFile, self.toPath))
shutil.copy(self.fromFile, self.toPath)
builder.AddCopy(entry_path, dest)
folders = [['addons', 'sourcemod', 'extensions'], ['addons', 'sourcemod', 'gamedata'], ['addons', 'sourcemod', 'scripting', 'include']]
folders = CreateFolders(['addons/sourcemod/extensions', 'addons/sourcemod/extensions/x64', 'addons/sourcemod/gamedata', 'addons/sourcemod/scripting', 'addons/sourcemod/scripting/include'])
#Setup
job.AddCommand(DestroyPath(os.path.join(AMBuild.outputFolder, 'package')))
job.AddCommand(CreateFolders(folders))
#Copy Files
job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'connect.games.txt'),
os.path.join('addons', 'sourcemod', 'gamedata')))
job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'connect.inc'),
os.path.join('addons', 'sourcemod', 'scripting', 'include')))
job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'connect.sp'),
os.path.join('addons', 'sourcemod', 'scripting')))
bincopies = []
def AddNormalLibrary(name, dest):
dest = os.path.join('addons', 'sourcemod', dest)
bincopies.append(CopyFile(os.path.join('..', name, name + osutil.SharedLibSuffix()), dest))
# Each platform's version of dump_syms needs the path in a different format.
if AMBuild.target['platform'] == 'linux':
debug_info.append(name + '/' + name + '.so')
elif AMBuild.target['platform'] == 'darwin':
debug_info.append(name + '/' + name + '.dylib.dSYM')
elif AMBuild.target['platform'] == 'windows':
debug_info.append(name + '\\' + name + '.pdb')
def AddHL2Library(name, dest):
for i in SM.sdkInfo:
sdk = SM.sdkInfo[i]
if AMBuild.target['platform'] not in sdk['platform']:
continue
AddNormalLibrary(name + '.ext.' + sdk['ext'], dest)
debug_info = []
AddHL2Library('connect', 'extensions')
job.AddCommandGroup(bincopies)
pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'wt')
for pdb in debug_info:
pdblog.write(pdb + '\n')
pdblog = open(os.path.join(builder.buildPath, 'pdblog.txt'), 'wt')
for cxx_task in Extension.extensions:
if cxx_task.target.arch == 'x86_64':
builder.AddCopy(cxx_task.binary, folders['addons/sourcemod/extensions/x64'])
else:
builder.AddCopy(cxx_task.binary, folders['addons/sourcemod/extensions'])
pdblog.write(cxx_task.debug.path + '\n')
pdblog.close()
CopyFiles('connect.games.txt', folders['addons/sourcemod/gamedata'])
CopyFiles('connect.inc', folders['addons/sourcemod/scripting/include'])
CopyFiles('connect.sp', folders['addons/sourcemod/scripting'])
debug_info = []

View File

@ -1,55 +1,50 @@
# vim: set ts=2 sw=2 tw=99 noet ft=python:
import os
# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python:
import os, sys
import re
import subprocess
from ambuild.cache import Cache
import ambuild.command as command
#Quickly try to ascertain the current repository revision
def GetVersion():
args = ['hg', 'parent', '-R', AMBuild.sourceFolder]
p = command.RunDirectCommand(AMBuild, args)
m = re.match('changeset:\s+(\d+):(.+)', p.stdoutText)
if m == None:
raise Exception('Could not determine repository version')
return m.groups()
builder.SetBuildFolder('/')
includes = builder.AddFolder('includes')
def PerformReversioning():
rev, cset = GetVersion()
cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache')
cache = Cache(cacheFile)
if os.path.isfile(cacheFile):
cache.LoadCache()
if cache.HasVariable('cset') and cache['cset'] == cset:
return False
cache.CacheVariable('cset', cset)
argv = [
sys.executable,
os.path.join(builder.sourcePath, 'buildbot', 'generate_header.py'),
os.path.join(builder.sourcePath),
os.path.join(builder.buildPath, 'includes'),
]
productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
productContents = productFile.read()
productFile.close()
m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
if m == None:
raise Exception('Could not detremine product version')
major, minor, release, tag = m.groups()
outputs = [
os.path.join(builder.buildFolder, 'includes', 'version_auto.h')
]
incFolder = os.path.join(AMBuild.sourceFolder, 'extension')
incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w')
incFile.write("""
#ifndef _AUTO_VERSION_INFORMATION_H_
#define _AUTO_VERSION_INFORMATION_H_
repo_head_path = os.path.join(builder.sourcePath, 'product.version')
if os.path.exists(os.path.join(builder.sourcePath, '.git')):
with open(os.path.join(builder.sourcePath, '.git', 'HEAD')) as fp:
head_contents = fp.read().strip()
if re.search('^[a-fA-F0-9]{40}$', head_contents):
repo_head_path = os.path.join(builder.sourcePath, '.git', 'HEAD')
else:
git_state = head_contents.split(':')[1].strip()
repo_head_path = os.path.join(builder.sourcePath, '.git', git_state)
if not os.path.exists(repo_head_path):
repo_head_path = os.path.join(builder.sourcePath, '.git', 'HEAD')
#define SM_BUILD_TAG \"{0}\"
#define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG
#define SM_VERSION \"{3}.{4}.{5}\"
#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
#define SM_FILE_VERSION {6},{7},{8},0
sources = [
os.path.join(builder.sourcePath, 'product.version'),
repo_head_path,
argv[1]
]
#endif /* _AUTO_VERSION_INFORMATION_H_ */
""".format(tag, rev, cset, major, minor, release, major, minor, release))
incFile.close()
cache.WriteCache()
PerformReversioning()
for source in sources:
if not os.path.exists(source):
print(source)
for source in sources:
if not os.path.exists(source):
print(source)
output_nodes = builder.AddCommand(
inputs=sources,
argv=argv,
outputs=outputs
)
rvalue = output_nodes

View File

@ -0,0 +1,61 @@
# vim: set ts=2 sw=2 tw=99 noet ft=python:
import os, sys
import re
import subprocess
argv = sys.argv[1:]
if len(argv) < 2:
sys.stderr.write('Usage: generate_header.py <source_path> <output_folder>\n')
sys.exit(1)
SourceFolder = os.path.abspath(os.path.normpath(argv[0]))
OutputFolder = os.path.normpath(argv[1])
def run_and_return(argv):
text = subprocess.check_output(argv)
if str != bytes:
text = str(text, 'utf-8')
return text.strip()
def GetGHVersion():
p = run_and_return(['hg', 'parent', '-R', SourceFolder])
m = re.match('changeset:\s+(\d+):(.+)', p.stdoutText)
if m == None:
raise Exception('Could not determine repository version')
return m.groups()
def GetGitVersion():
revision_count = run_and_return(['git', 'rev-list', '--count', 'HEAD'])
revision_hash = run_and_return(['git', 'log', '--pretty=format:%h:%H', '-n', '1'])
shorthash, longhash = revision_hash.split(':')
return revision_count, shorthash
rev = None
cset = None
rev, cset = GetGitVersion()
productFile = open(os.path.join(SourceFolder, 'product.version'), 'r')
productContents = productFile.read()
productFile.close()
m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
if m == None:
raise Exception('Could not detremine product version')
major, minor, release, tag = m.groups()
incFile = open(os.path.join(OutputFolder, 'version_auto.h'), 'w')
incFile.write("""
#ifndef _AUTO_VERSION_INFORMATION_H_
#define _AUTO_VERSION_INFORMATION_H_
#define SM_BUILD_TAG \"{0}\"
#define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG
#define SM_VERSION \"{3}.{4}.{5}\"
#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
#define SM_FILE_VERSION {6},{7},{8},0
#endif /* _AUTO_VERSION_INFORMATION_H_ */
""".format(tag, rev, cset, major, minor, release, major, minor, release))
incFile.close()
filename_versioning = open(os.path.join(OutputFolder, 'filename_versioning.txt'), 'w')
filename_versioning.write("{0}.{1}.{2}-git{3}-{4}".format(major, minor, release, rev, cset))
filename_versioning.close()

View File

@ -1,10 +1,26 @@
# vim: set ts=2 sw=2 tw=99 noet:
# vim: set sts=2 ts=8 sw=2 tw=99 et:
import sys
import ambuild.runner as runner
from ambuild2 import run
run = runner.Runner()
run.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
# Simple extensions do not need to modify this file.
parser = run.BuildParser(sourcePath = sys.path[0], api='2.2')
parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
help='Root search folder for HL2SDKs')
parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None,
help='Path to Metamod:Source')
parser.options.add_argument('--sm-path', type=str, dest='sm_path', default=None,
help='Path to SourceMod')
parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
help='Enable debugging symbols')
run.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt',
parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt',
help='Enable optimization')
run.Configure(sys.path[0])
parser.options.add_argument('--enable-auto-versioning', action='store_false', dest='disable_auto_versioning',
default=True, help='Enables the auto versioning script')
parser.options.add_argument('-s', '--sdks', default='all', dest='sdks',
help='Build against specified SDKs; valid args are "all", "present", or '
'space-delimited list of engine names (default: %default)')
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
help="Override the target architecture (use commas to separate multiple targets).")
parser.Configure()

View File

@ -15,14 +15,14 @@
"ISteamGameServer__BeginAuthSession"
{
"linux" "29"
"mac" "29"
"linux64" "29"
"windows" "29"
}
"ISteamGameServer__EndAuthSession"
{
"linux" "30"
"mac" "30"
"linux64" "30"
"windows" "30"
}
}
@ -39,7 +39,7 @@
{
"library" "engine"
"linux" "@_Z12Steam3Serverv"
"mac" "@_Z12Steam3Serverv"
"linux64" "@_Z12Steam3Serverv"
}
}
}
@ -102,7 +102,7 @@
{
"library" "engine"
"linux" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i"
"mac" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i"
"linux64" "@_ZN11CBaseServer13ConnectClientER8netadr_siiiiPKcS3_S3_i"
"windows" "\x55\x8B\xEC\x81\xEC\x24\x05\x00\x00"
}
@ -110,7 +110,7 @@
{
"library" "engine"
"linux" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc"
"mac" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc"
"linux64" "@_ZN11CBaseServer16RejectConnectionERK8netadr_siPKc"
"windows" "\x55\x8B\xEC\x81\xEC\x04\x05\x00\x00\x57"
}
@ -118,7 +118,7 @@
{
"library" "engine"
"linux" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID"
"mac" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID"
"linux64" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID"
"windows" "\x55\x8B\xEC\x56\x57\x8B\x7D\x08\x8B\xF1\x8D\x4E\x04"
}
@ -126,7 +126,7 @@
{
"library" "engine"
"linux" "@_ZN11CBaseServer18CheckChallengeTypeEP11CBaseClientiR8netadr_siPKcii"
"mac" "@_ZN11CBaseServer18CheckChallengeTypeEP11CBaseClientiR8netadr_siPKcii"
"linux64" "@_ZN11CBaseServer18CheckChallengeTypeEP11CBaseClientiR8netadr_siPKcii"
"windows" "\x55\x8B\xEC\x83\xEC\x14\x53\x8B\x5D\x14"
}
}

View File

@ -1,38 +1,29 @@
# vim: set ts=2 sw=2 tw=99 noet ft=python:
import os
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os, sys
for i in SM.sdkInfo:
sdk = SM.sdkInfo[i]
if AMBuild.target['platform'] not in sdk['platform']:
projectname = 'connect'
project = builder.LibraryProject(projectname + '.ext')
project.sources = [
'extension.cpp',
os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp'),
os.path.join(Extension.sm_root, 'public', 'CDetour', 'detours.cpp'),
os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'decode.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'itab.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'syn-att.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'syn-intel.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'syn.c'),
os.path.join(Extension.sm_root, 'public', 'libudis86', 'udis86.c')
]
for sdk_name in Extension.sdks:
sdk = Extension.sdks[sdk_name]
for cxx in builder.targets:
if not cxx.target.arch in sdk.platformSpec[cxx.target.platform]:
continue
compiler = SM.DefaultHL2Compiler('extension', i)
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'extension'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'extension', 'sdk'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.cache['SOURCEMOD14'], 'public'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.cache['SOURCEMOD14'], 'extensions'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.cache['SOURCEMOD14'], 'public', 'sourcepawn'))
if compiler.cc.name == 'gcc':
compiler['CFLAGS'].append('-Wno-parentheses')
if i != 'ep1':
compiler['CDEFINES'].append('HOOKING_ENABLED')
name = 'connect.ext.' + sdk['ext']
extension = AMBuild.AddJob(name)
binary = Cpp.LibraryBuilder(name, AMBuild, extension, compiler)
SM.PreSetupHL2Job(extension, binary, i)
binary.AddSourceFiles('extension', [
'extension.cpp',
'asm/asm.c',
'CDetour/detours.cpp',
'sdk/smsdk_ext.cpp'
])
SM.PostSetupHL2Job(extension, binary, i)
SM.AutoVersion('extension', binary)
SM.ExtractDebugInfo(extension, binary)
binary.SendToJob()
binary = Extension.HL2ExtConfig(project, builder, cxx, projectname + '.ext.' + sdk.ext, sdk)
Extension.extensions = builder.Add(project)

View File

@ -1,98 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id: detourhelpers.h 248 2008-08-27 00:56:22Z pred $
*/
#ifndef _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
#define _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
#if defined PLATFORM_POSIX
#include <sys/mman.h>
#define PAGE_SIZE 4096
#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
#endif
struct patch_t
{
patch_t()
{
patch[0] = 0;
bytes = 0;
}
unsigned char patch[20];
size_t bytes;
};
inline void ProtectMemory(void *addr, int length, int prot)
{
#if defined PLATFORM_POSIX
void *addr2 = (void *)ALIGN(addr);
mprotect(addr2, sysconf(_SC_PAGESIZE), prot);
#elif defined PLATFORM_WINDOWS
DWORD old_prot;
VirtualProtect(addr, length, prot, &old_prot);
#endif
}
inline void SetMemPatchable(void *address, size_t size)
{
ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
}
inline void DoGatePatch(unsigned char *target, void *callback)
{
SetMemPatchable(target, 20);
target[0] = 0xFF; /* JMP */
target[1] = 0x25; /* MEM32 */
*(void **)(&target[2]) = callback;
}
inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
{
ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE);
unsigned char *addr = (unsigned char *)address + offset;
if (restore)
{
for (size_t i=0; i<patch->bytes; i++)
{
restore->patch[i] = addr[i];
}
restore->bytes = patch->bytes;
}
for (size_t i=0; i<patch->bytes; i++)
{
addr[i] = patch->patch[i];
}
}
#endif //_INCLUDE_SOURCEMOD_DETOURHELPERS_H_

View File

@ -1,192 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id: detours.cpp 248 2008-08-27 00:56:22Z pred $
*/
#include "detours.h"
#include <asm/asm.h>
ISourcePawnEngine *CDetourManager::spengine = NULL;
IGameConfig *CDetourManager::gameconf = NULL;
void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
{
CDetourManager::spengine = spengine;
CDetourManager::gameconf = gameconf;
}
CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, const char *signame)
{
CDetour *detour = new CDetour(callbackfunction, trampoline, signame);
if (detour)
{
if (!detour->Init(spengine, gameconf))
{
delete detour;
return NULL;
}
return detour;
}
return NULL;
}
CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame)
{
enabled = false;
detoured = false;
detour_address = NULL;
detour_trampoline = NULL;
this->signame = signame;
this->detour_callback = callbackfunction;
spengine = NULL;
gameconf = NULL;
this->trampoline = trampoline;
}
bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
{
this->spengine = spengine;
this->gameconf = gameconf;
if (!CreateDetour())
{
enabled = false;
return enabled;
}
enabled = true;
return enabled;
}
void CDetour::Destroy()
{
DeleteDetour();
delete this;
}
bool CDetour::IsEnabled()
{
return enabled;
}
bool CDetour::CreateDetour()
{
if (!gameconf->GetMemSig(signame, &detour_address))
{
g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
return false;
}
if (!detour_address)
{
g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame);
return false;
}
detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
/* First, save restore bits */
for (size_t i=0; i<detour_restore.bytes; i++)
{
detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
}
JitWriter wr;
JitWriter *jit = &wr;
jit_uint32_t CodeSize = 0;
wr.outbase = NULL;
wr.outptr = NULL;
jit_rewind:
/* Patch old bytes in */
if (wr.outbase != NULL)
{
copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
}
wr.outptr += detour_restore.bytes;
/* Return to the original function */
jitoffs_t call = IA32_Jump_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
if (wr.outbase == NULL)
{
CodeSize = wr.get_outputpos();
wr.outbase = (jitcode_t)spengine->AllocatePageMemory(CodeSize);
spengine->SetReadWrite(wr.outbase);
wr.outptr = wr.outbase;
detour_trampoline = wr.outbase;
goto jit_rewind;
}
spengine->SetReadExecute(wr.outbase);
*trampoline = detour_trampoline;
return true;
}
void CDetour::DeleteDetour()
{
if (detoured)
{
DisableDetour();
}
if (detour_trampoline)
{
/* Free the allocated trampoline memory */
spengine->FreePageMemory(detour_trampoline);
detour_trampoline = NULL;
}
}
void CDetour::EnableDetour()
{
if (!detoured)
{
DoGatePatch((unsigned char *)detour_address, &detour_callback);
detoured = true;
}
}
void CDetour::DisableDetour()
{
if (detoured)
{
/* Remove the patch */
ApplyPatch(detour_address, 0, &detour_restore, NULL);
detoured = false;
}
}

View File

@ -1,300 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id: detours.h 257 2008-09-23 03:12:13Z pred $
*/
#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
#define _INCLUDE_SOURCEMOD_DETOURS_H_
#include "extension.hpp"
#include <jit/jit_helpers.h>
#include <jit/x86/x86_macros.h>
#include "detourhelpers.h"
/**
* CDetours class for SourceMod Extensions by pRED*
* detourhelpers.h entirely stolen from CSS:DM and were written by BAILOPAN (I assume).
* asm.h/c from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -fPIC thunks correctly
* Concept by Nephyrin Zey (http://www.doublezen.net/) and Windows Detour Library (http://research.microsoft.com/sn/detours/)
* Member function pointer ideas by Don Clugston (http://www.codeproject.com/cpp/FastDelegate.asp)
*/
#define DETOUR_MEMBER_CALL(name) (this->*name##_Actual)
#define DETOUR_STATIC_CALL(name) (name##_Actual)
#define DETOUR_DECL_STATIC0(name, ret) \
ret (*name##_Actual)(void) = NULL; \
ret name(void)
#define DETOUR_DECL_STATIC1(name, ret, p1type, p1name) \
ret (*name##_Actual)(p1type) = NULL; \
ret name(p1type p1name)
#define DETOUR_DECL_STATIC2(name, ret, p1type, p1name, p2type, p2name) \
ret (*name##_Actual)(p1type, p2type) = NULL; \
ret name(p1type p1name, p2type p2name)
#define DETOUR_DECL_MEMBER0(name, ret) \
class name##Class \
{ \
public: \
ret name(); \
static ret (name##Class::* name##_Actual)(void); \
}; \
ret (name##Class::* name##Class::name##_Actual)(void) = NULL; \
ret name##Class::name()
#define DETOUR_DECL_MEMBER1(name, ret, p1type, p1name) \
class name##Class \
{ \
public: \
ret name(p1type p1name); \
static ret (name##Class::* name##_Actual)(p1type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type) = NULL; \
ret name##Class::name(p1type p1name)
#define DETOUR_DECL_MEMBER2(name, ret, p1type, p1name, p2type, p2name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name); \
static ret (name##Class::* name##_Actual)(p1type, p2type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name)
#define DETOUR_DECL_MEMBER3(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name)
#define DETOUR_DECL_MEMBER5(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name)
#define DETOUR_DECL_MEMBER7(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name)
#define DETOUR_DECL_MEMBER8(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name)
#define DETOUR_DECL_MEMBER9(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name, p9type, p9name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name)
#define DETOUR_DECL_MEMBER10(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name, p9type, p9name, p10type, p10name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name, p10type p10name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type, p10type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type, p10type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name, p10type p10name)
#define GET_MEMBER_CALLBACK(name) (void *)GetCodeAddress(&name##Class::name)
#define GET_MEMBER_TRAMPOLINE(name) (void **)(&name##Class::name##_Actual)
#define GET_STATIC_CALLBACK(name) (void *)&name
#define GET_STATIC_TRAMPOLINE(name) (void **)&name##_Actual
#define DETOUR_CREATE_MEMBER(name, gamedata) CDetourManager::CreateDetour(GET_MEMBER_CALLBACK(name), GET_MEMBER_TRAMPOLINE(name), gamedata);
#define DETOUR_CREATE_STATIC(name, gamedata) CDetourManager::CreateDetour(GET_STATIC_CALLBACK(name), GET_STATIC_TRAMPOLINE(name), gamedata);
class GenericClass {};
typedef void (GenericClass::*VoidFunc)();
inline void *GetCodeAddr(VoidFunc mfp)
{
return *(void **)&mfp;
}
/**
* Converts a member function pointer to a void pointer.
* This relies on the assumption that the code address lies at mfp+0
* This is the case for both g++ and later MSVC versions on non virtual functions but may be different for other compilers
* Based on research by Don Clugston : http://www.codeproject.com/cpp/FastDelegate.asp
*/
#define GetCodeAddress(mfp) GetCodeAddr(reinterpret_cast<VoidFunc>(mfp))
class CDetourManager;
class CDetour
{
public:
bool IsEnabled();
/**
* These would be somewhat self-explanatory I hope
*/
void EnableDetour();
void DisableDetour();
void Destroy();
friend class CDetourManager;
protected:
CDetour(void *callbackfunction, void **trampoline, const char *signame);
bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
private:
/* These create/delete the allocated memory */
bool CreateDetour();
void DeleteDetour();
bool enabled;
bool detoured;
patch_t detour_restore;
/* Address of the detoured function */
void *detour_address;
/* Address of the allocated trampoline function */
void *detour_trampoline;
/* Address of the callback handler */
void *detour_callback;
/* The function pointer used to call our trampoline */
void **trampoline;
const char *signame;
ISourcePawnEngine *spengine;
IGameConfig *gameconf;
};
class CDetourManager
{
public:
static void Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
/**
* Creates a new detour
*
* @param callbackfunction Void pointer to your detour callback function.
* @param trampoline Address of the trampoline pointer
* @param signame Section name containing a signature to fetch from the gamedata file.
* @return A new CDetour pointer to control your detour.
*
* Example:
*
* CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int)
*
* Define a new class with the required function and a member function pointer to the same type:
*
* class CBaseServerDetour
* {
* public:
* bool ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
* static bool (CBaseServerDetour::* ConnectClient_Actual)(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
* }
*
* void *callbackfunc = GetCodeAddress(&CBaseServerDetour::ConnectClient);
* void **trampoline = (void **)(&CBaseServerDetour::ConnectClient_Actual);
*
* Creation:
* CDetourManager::CreateDetour(callbackfunc, trampoline, "ConnectClient");
*
* Usage:
*
* CBaseServerDetour::ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int)
* {
* //pre hook code
* bool result = (this->*ConnectClient_Actual)(netaddr_s, rest of params);
* //post hook code
* return result;
* }
*
* Note we changed the netadr_s reference into a void* to avoid needing to define the type
*/
static CDetour *CreateDetour(void *callbackfunction, void **trampoline, const char *signame);
friend class CBlocker;
friend class CDetour;
private:
static ISourcePawnEngine *spengine;
static IGameConfig *gameconf;
};
#define DECL_DETOUR(name) \
CDetour *name##_Detour = NULL;
#define CREATE_DETOUR(name) \
name##_Detour = DETOUR_CREATE_MEMBER(name, #name); \
if (name##_Detour != NULL) \
{ \
name##_Detour->EnableDetour(); \
} else { \
snprintf(error, maxlen, "Failed to create " #name " detour, check error log.\n"); \
return false; \
}
#define DESTROY_DETOUR(name) \
if (name##_Detour != NULL) \
{ \
name##_Detour->Destroy(); \
name##_Detour = NULL; \
}
#endif // _INCLUDE_SOURCEMOD_DETOURS_H_

View File

@ -1,421 +0,0 @@
#include "asm.h"
#ifndef WIN32
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#define REG_EAX 0
#define REG_ECX 1
#define REG_EDX 2
#define REG_EBX 3
#define IA32_MOV_REG_IMM 0xB8 // encoding is +r <imm32>
#endif
extern void Msg( const char *, ... );
/**
* Checks if a call to a fpic thunk has just been written into dest.
* If found replaces it with a direct mov that sets the required register to the value of pc.
*
* @param dest Destination buffer where a call opcode + addr (5 bytes) has just been written.
* @param pc The program counter value that needs to be set (usually the next address from the source).
* @noreturn
*/
void check_thunks(unsigned char *dest, unsigned char *pc)
{
#if defined WIN32
return;
#else
/* Step write address back 4 to the start of the function address */
unsigned char *writeaddr = dest - 4;
unsigned char *calloffset = *(unsigned char **)writeaddr;
unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
/* Lookup name of function being called */
if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
{
//a thunk maybe?
char movByte = IA32_MOV_REG_IMM;
/* Calculate the correct mov opcode */
switch (*(calladdr+1))
{
case 0x04:
{
movByte += REG_EAX;
break;
}
case 0x1C:
{
movByte += REG_EBX;
break;
}
case 0x0C:
{
movByte += REG_ECX;
break;
}
case 0x14:
{
movByte += REG_EDX;
break;
}
default:
{
Msg("Unknown thunk: %c\n", *(calladdr+1));
break;
}
}
/* Move our write address back one to where the call opcode was */
writeaddr--;
/* Write our mov */
*writeaddr = movByte;
writeaddr++;
/* Write the value - The provided program counter value */
*(void **)writeaddr = (void *)pc;
writeaddr += 4;
}
return;
#endif
}
//if dest is NULL, returns minimum number of bytes needed to be copied
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
//http://www.devmaster.net/forums/showthread.php?t=2311
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
int bytecount = 0;
while(bytecount < required_len && *func != 0xCC)
{
// prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
int operandSize = 4;
int FPU = 0;
int twoByte = 0;
unsigned char opcode = 0x90;
unsigned char modRM = 0xFF;
while(*func == 0xF0 ||
*func == 0xF2 ||
*func == 0xF3 ||
(*func & 0xFC) == 0x64 ||
(*func & 0xF8) == 0xD8 ||
(*func & 0x7E) == 0x62)
{
if(*func == 0x66)
{
operandSize = 2;
}
else if((*func & 0xF8) == 0xD8)
{
FPU = *func;
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
break;
}
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// two-byte opcode byte
if(*func == 0x0F)
{
twoByte = 1;
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// opcode byte
opcode = *func++;
if (dest) *dest++ = opcode;
bytecount++;
// mod R/M byte
modRM = 0xFF;
if(FPU)
{
if((opcode & 0xC0) != 0xC0)
{
modRM = opcode;
}
}
else if(!twoByte)
{
if((opcode & 0xC4) == 0x00 ||
((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) ||
(opcode & 0xF0) == 0x80 ||
((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) ||
(opcode & 0xFC) == 0xD0 ||
(opcode & 0xF6) == 0xF6)
{
modRM = *func++;
if (dest) *dest++ = modRM;
bytecount++;
}
}
else
{
if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) ||
(opcode & 0xF0) == 0x30 ||
opcode == 0x77 ||
(opcode & 0xF0) == 0x80 ||
((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) ||
(opcode & 0xF8) == 0xC8)
{
// No mod R/M byte
}
else
{
modRM = *func++;
if (dest) *dest++ = modRM;
bytecount++;
}
}
// SIB
if((modRM & 0x07) == 0x04 &&
(modRM & 0xC0) != 0xC0)
{
if (dest)
*dest++ = *func++; //SIB
else
func++;
bytecount++;
}
// mod R/M displacement
// Dword displacement, no base
if((modRM & 0xC5) == 0x05) {
if (dest) {
*(unsigned int*)dest = *(unsigned int*)func;
dest += 4;
}
func += 4;
bytecount += 4;
}
// Byte displacement
if((modRM & 0xC0) == 0x40) {
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
// Dword displacement
if((modRM & 0xC0) == 0x80) {
if (dest) {
*(unsigned int*)dest = *(unsigned int*)func;
dest += 4;
}
func += 4;
bytecount += 4;
}
// immediate
if(FPU)
{
// Can't have immediate operand
}
else if(!twoByte)
{
if((opcode & 0xC7) == 0x04 ||
(opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL
(opcode & 0xF0) == 0x70 || // Jcc
opcode == 0x80 ||
opcode == 0x83 ||
(opcode & 0xFD) == 0xA0 || // MOV
opcode == 0xA8 || // TEST
(opcode & 0xF8) == 0xB0 || // MOV
(opcode & 0xFE) == 0xC0 || // RCL
opcode == 0xC6 || // MOV
opcode == 0xCD || // INT
(opcode & 0xFE) == 0xD4 || // AAD/AAM
(opcode & 0xF8) == 0xE0 || // LOOP/JCXZ
opcode == 0xEB ||
(opcode == 0xF6 && (modRM & 0x30) == 0x00)) // TEST
{
if (dest)
*dest++ = *func++;
else
func++;
bytecount++;
}
else if((opcode & 0xF7) == 0xC2) // RET
{
if (dest) {
*(unsigned short*)dest = *(unsigned short*)func;
dest += 2;
}
func += 2;
bytecount += 2;
}
else if((opcode & 0xFC) == 0x80 ||
(opcode & 0xC7) == 0x05 ||
(opcode & 0xF8) == 0xB8 ||
(opcode & 0xFE) == 0xE8 || // CALL/Jcc
(opcode & 0xFE) == 0x68 ||
(opcode & 0xFC) == 0xA0 ||
(opcode & 0xEE) == 0xA8 ||
opcode == 0xC7 ||
(opcode == 0xF7 && (modRM & 0x30) == 0x00))
{
if (dest) {
//Fix CALL/JMP offset
if ((opcode & 0xFE) == 0xE8) {
if (operandSize == 4)
{
*(long*)dest = ((func + *(long*)func) - dest);
//pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
check_thunks(dest+4, func+4);
}
else
*(short*)dest = ((func + *(short*)func) - dest);
} else {
if (operandSize == 4)
*(unsigned long*)dest = *(unsigned long*)func;
else
*(unsigned short*)dest = *(unsigned short*)func;
}
dest += operandSize;
}
func += operandSize;
bytecount += operandSize;
}
}
else
{
if(opcode == 0xBA || // BT
opcode == 0x0F || // 3DNow!
(opcode & 0xFC) == 0x70 || // PSLLW
(opcode & 0xF7) == 0xA4 || // SHLD
opcode == 0xC2 ||
opcode == 0xC4 ||
opcode == 0xC5 ||
opcode == 0xC6)
{
if (dest)
*dest++ = *func++;
else
func++;
}
else if((opcode & 0xF0) == 0x80) // Jcc -i
{
if (dest) {
if (operandSize == 4)
*(unsigned long*)dest = *(unsigned long*)func;
else
*(unsigned short*)dest = *(unsigned short*)func;
dest += operandSize;
}
func += operandSize;
bytecount += operandSize;
}
}
}
return bytecount;
}
//insert a specific JMP instruction at the given location
void inject_jmp(void* src, void* dest) {
*(unsigned char*)src = OP_JMP;
*(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
}
//fill a given block with NOPs
void fill_nop(void* src, unsigned int len) {
unsigned char* src2 = (unsigned char*)src;
while (len) {
*src2++ = OP_NOP;
--len;
}
}
void* eval_jump(void* src) {
unsigned char* addr = (unsigned char*)src;
if (!addr) return 0;
//import table jump
if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
addr += 2;
addr = *(unsigned char**)addr;
//TODO: if addr points into the IAT
return *(void**)addr;
}
//8bit offset
else if (addr[0] == OP_JMP_BYTE) {
addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
//mangled 32bit jump?
if (addr[0] == OP_JMP) {
addr = addr + *(int*)&addr[1];
}
return addr;
}
/*
//32bit offset
else if (addr[0] == OP_JMP) {
addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
}
*/
return addr;
}
/*
from ms detours package
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
return false;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
return false;
}
}
*/

View File

@ -1,40 +0,0 @@
#ifndef __ASM_H__
#define __ASM_H__
#define OP_JMP 0xE9
#define OP_JMP_SIZE 5
#define OP_NOP 0x90
#define OP_NOP_SIZE 1
#define OP_PREFIX 0xFF
#define OP_JMP_SEG 0x25
#define OP_JMP_BYTE 0xEB
#define OP_JMP_BYTE_SIZE 2
#ifdef __cplusplus
extern "C" {
#endif
void check_thunks(unsigned char *dest, unsigned char *pc);
//if dest is NULL, returns minimum number of bytes needed to be copied
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
//http://www.devmaster.net/forums/showthread.php?t=2311
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len);
//insert a specific JMP instruction at the given location
void inject_jmp(void* src, void* dest);
//fill a given block with NOPs
void fill_nop(void* src, unsigned int len);
//evaluate a JMP at the target
void* eval_jump(void* src);
#ifdef __cplusplus
}
#endif
#endif //__ASM_H__

View File

@ -17,7 +17,7 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "extension.hpp"
#include "extension.h"
#include "CDetour/detours.h"
#include "steam/steamclientpublic.h"
@ -44,6 +44,7 @@ typedef enum EAuthProtocol
k_EAuthProtocolSteam = 3,
} EAuthProtocol;
#if SOURCE_ENGINE < SE_SDK2013 || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
typedef enum EBeginAuthSessionResult
{
k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID.
@ -53,6 +54,7 @@ typedef enum EBeginAuthSessionResult
k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game
k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired
} EBeginAuthSessionResult;
#endif
typedef struct netadr_s
{
@ -74,7 +76,11 @@ public:
const char *CSteamID::Render() const
{
static char szSteamID[64];
#if SOURCE_ENGINE < SE_SDK2013 || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
V_snprintf(szSteamID, sizeof(szSteamID), "STEAM_0:%u:%u", (m_unAccountID % 2) ? 1 : 0, (int32)m_unAccountID/2);
#else
V_snprintf(szSteamID, sizeof(szSteamID), "STEAM_0:%u:%u", (m_steamid.m_comp.m_unAccountID % 2) ? 1 : 0, (int32)m_steamid.m_comp.m_unAccountID/2);
#endif
return szSteamID;
}
@ -211,9 +217,9 @@ void EndAuthSession(CSteamID steamID)
return (void)(reinterpret_cast<VFuncEmptyClass*>(this_ptr)->*u.mfpnew)(steamID);
}
DECL_DETOUR(CBaseServer__ConnectClient);
DECL_DETOUR(CBaseServer__RejectConnection)
DECL_DETOUR(CBaseServer__CheckChallengeType);
CDetour* detourCBaseServer__ConnectClient = nullptr;
CDetour* detourCBaseServer__RejectConnection = nullptr;
CDetour* detourCBaseServer__CheckChallengeType = nullptr;
bool g_bEndAuthSessionOnRejectConnection = false;
CSteamID g_lastClientSteamID;
@ -221,7 +227,18 @@ CSteamID g_lastClientSteamID;
bool g_bSuppressCheckChallengeType = false;
char passwordBuffer[255];
DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient *, netadr_t &, address, int, nProtocol, int, iChallenge, int, iClientChallenge, int, nAuthProtocol, const char *, pchName, const char *, pchPassword, const char *, pCookie, int, cbCookie)
#define DETOUR_DECL_MEMBER9(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name, p9type, p9name) \
class name##Class \
{ \
public: \
ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name); \
static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type); \
}; \
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name)
DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, int, nProtocol, int, iChallenge, int, iClientChallenge, int, nAuthProtocol, const char *, pchName, const char *, pchPassword, const char *, pCookie, int, cbCookie)
{
if (nAuthProtocol != k_EAuthProtocolSteam)
{
@ -379,9 +396,9 @@ bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
CREATE_DETOUR(CBaseServer__ConnectClient);
CREATE_DETOUR(CBaseServer__RejectConnection);
CREATE_DETOUR(CBaseServer__CheckChallengeType);
detourCBaseServer__ConnectClient = DETOUR_CREATE_MEMBER(CBaseServer__ConnectClient, "CBaseServer__ConnectClient");
detourCBaseServer__RejectConnection = DETOUR_CREATE_MEMBER(CBaseServer__RejectConnection, "CBaseServer__RejectConnection");
detourCBaseServer__CheckChallengeType = DETOUR_CREATE_MEMBER(CBaseServer__CheckChallengeType, "CBaseServer__CheckChallengeType");
g_pConnectForward = g_pForwards->CreateForward("OnClientPreConnectEx", ET_LowEvent, 5, NULL, Param_String, Param_String, Param_String, Param_String, Param_String);
@ -406,9 +423,21 @@ void Connect::SDK_OnUnload()
bool Connect::SDK_OnMetamodUnload(char *error, size_t maxlen)
{
DESTROY_DETOUR(CBaseServer__ConnectClient);
DESTROY_DETOUR(CBaseServer__RejectConnection);
DESTROY_DETOUR(CBaseServer__CheckChallengeType);
if (detourCBaseServer__ConnectClient)
{
detourCBaseServer__ConnectClient->DisableDetour();
delete detourCBaseServer__ConnectClient;
}
if (detourCBaseServer__RejectConnection)
{
detourCBaseServer__RejectConnection->DisableDetour();
delete detourCBaseServer__RejectConnection;
}
if (detourCBaseServer__CheckChallengeType)
{
detourCBaseServer__CheckChallengeType->DisableDetour();
delete detourCBaseServer__CheckChallengeType;
}
return true;
}

View File

@ -26,7 +26,7 @@
* @brief Connect extension code header.
*/
#include "smsdk_ext.hpp"
#include "smsdk_ext.h"
/**
* @brief Sample implementation of the SDK Extension.

View File

@ -1,475 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include "smsdk_ext.hpp"
/**
* @file smsdk_ext.cpp
* @brief Contains wrappers for making Extensions easier to write.
*/
IExtension *myself = NULL; /**< Ourself */
IShareSys *g_pShareSys = NULL; /**< Share system */
IShareSys *sharesys = NULL; /**< Share system */
ISourceMod *g_pSM = NULL; /**< SourceMod helpers */
ISourceMod *smutils = NULL; /**< SourceMod helpers */
#if defined SMEXT_ENABLE_FORWARDSYS
IForwardManager *g_pForwards = NULL; /**< Forward system */
IForwardManager *forwards = NULL; /**< Forward system */
#endif
#if defined SMEXT_ENABLE_HANDLESYS
IHandleSys *g_pHandleSys = NULL; /**< Handle system */
IHandleSys *handlesys = NULL; /**< Handle system */
#endif
#if defined SMEXT_ENABLE_PLAYERHELPERS
IPlayerManager *playerhelpers = NULL; /**< Player helpers */
#endif //SMEXT_ENABLE_PLAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
IDBManager *dbi = NULL; /**< DB Manager */
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
IGameConfigManager *gameconfs = NULL; /**< Game config manager */
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_MEMUTILS
IMemoryUtils *memutils = NULL;
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMEHELPERS
IGameHelpers *gamehelpers = NULL;
#endif
#if defined SMEXT_ENABLE_TIMERSYS
ITimerSystem *timersys = NULL;
#endif
#if defined SMEXT_ENABLE_ADTFACTORY
IADTFactory *adtfactory = NULL;
#endif
#if defined SMEXT_ENABLE_THREADER
IThreader *threader = NULL;
#endif
#if defined SMEXT_ENABLE_LIBSYS
ILibrarySys *libsys = NULL;
#endif
#if defined SMEXT_ENABLE_PLUGINSYS
SourceMod::IPluginManager *plsys;
#endif
#if defined SMEXT_ENABLE_MENUS
IMenuManager *menus = NULL;
#endif
#if defined SMEXT_ENABLE_ADMINSYS
IAdminSystem *adminsys = NULL;
#endif
#if defined SMEXT_ENABLE_TEXTPARSERS
ITextParsers *textparsers = NULL;
#endif
#if defined SMEXT_ENABLE_USERMSGS
IUserMessages *usermsgs = NULL;
#endif
#if defined SMEXT_ENABLE_TRANSLATOR
ITranslator *translator = NULL;
#endif
#if defined SMEXT_ENABLE_NINVOKE
INativeInterface *ninvoke = NULL;
#endif
/** Exports the main interface */
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
{
return g_pExtensionIface;
}
SDKExtension::SDKExtension()
{
#if defined SMEXT_CONF_METAMOD
m_SourceMMLoaded = false;
m_WeAreUnloaded = false;
m_WeGotPauseChange = false;
#endif
}
bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late)
{
g_pShareSys = sharesys = sys;
myself = me;
#if defined SMEXT_CONF_METAMOD
m_WeAreUnloaded = true;
if (!m_SourceMMLoaded)
{
if (error)
{
snprintf(error, maxlength, "Metamod attach failed");
}
return false;
}
#endif
SM_GET_IFACE(SOURCEMOD, g_pSM);
smutils = g_pSM;
#if defined SMEXT_ENABLE_HANDLESYS
SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys);
handlesys = g_pHandleSys;
#endif
#if defined SMEXT_ENABLE_FORWARDSYS
SM_GET_IFACE(FORWARDMANAGER, g_pForwards);
forwards = g_pForwards;
#endif
#if defined SMEXT_ENABLE_PLAYERHELPERS
SM_GET_IFACE(PLAYERMANAGER, playerhelpers);
#endif
#if defined SMEXT_ENABLE_DBMANAGER
SM_GET_IFACE(DBI, dbi);
#endif
#if defined SMEXT_ENABLE_GAMECONF
SM_GET_IFACE(GAMECONFIG, gameconfs);
#endif
#if defined SMEXT_ENABLE_MEMUTILS
SM_GET_IFACE(MEMORYUTILS, memutils);
#endif
#if defined SMEXT_ENABLE_GAMEHELPERS
SM_GET_IFACE(GAMEHELPERS, gamehelpers);
#endif
#if defined SMEXT_ENABLE_TIMERSYS
SM_GET_IFACE(TIMERSYS, timersys);
#endif
#if defined SMEXT_ENABLE_ADTFACTORY
SM_GET_IFACE(ADTFACTORY, adtfactory);
#endif
#if defined SMEXT_ENABLE_THREADER
SM_GET_IFACE(THREADER, threader);
#endif
#if defined SMEXT_ENABLE_LIBSYS
SM_GET_IFACE(LIBRARYSYS, libsys);
#endif
#if defined SMEXT_ENABLE_PLUGINSYS
SM_GET_IFACE(PLUGINSYSTEM, plsys);
#endif
#if defined SMEXT_ENABLE_MENUS
SM_GET_IFACE(MENUMANAGER, menus);
#endif
#if defined SMEXT_ENABLE_ADMINSYS
SM_GET_IFACE(ADMINSYS, adminsys);
#endif
#if defined SMEXT_ENABLE_TEXTPARSERS
SM_GET_IFACE(TEXTPARSERS, textparsers);
#endif
#if defined SMEXT_ENABLE_USERMSGS
SM_GET_IFACE(USERMSGS, usermsgs);
#endif
#if defined SMEXT_ENABLE_TRANSLATOR
SM_GET_IFACE(TRANSLATOR, translator);
#endif
if (SDK_OnLoad(error, maxlength, late))
{
#if defined SMEXT_CONF_METAMOD
m_WeAreUnloaded = true;
#endif
return true;
}
return false;
}
bool SDKExtension::IsMetamodExtension()
{
#if defined SMEXT_CONF_METAMOD
return true;
#else
return false;
#endif
}
void SDKExtension::OnExtensionPauseChange(bool state)
{
#if defined SMEXT_CONF_METAMOD
m_WeGotPauseChange = true;
#endif
SDK_OnPauseChange(state);
}
void SDKExtension::OnExtensionsAllLoaded()
{
SDK_OnAllLoaded();
}
void SDKExtension::OnExtensionUnload()
{
#if defined SMEXT_CONF_METAMOD
m_WeAreUnloaded = true;
#endif
SDK_OnUnload();
}
const char *SDKExtension::GetExtensionAuthor()
{
return SMEXT_CONF_AUTHOR;
}
const char *SDKExtension::GetExtensionDateString()
{
return SMEXT_CONF_DATESTRING;
}
const char *SDKExtension::GetExtensionDescription()
{
return SMEXT_CONF_DESCRIPTION;
}
const char *SDKExtension::GetExtensionVerString()
{
return SMEXT_CONF_VERSION;
}
const char *SDKExtension::GetExtensionName()
{
return SMEXT_CONF_NAME;
}
const char *SDKExtension::GetExtensionTag()
{
return SMEXT_CONF_LOGTAG;
}
const char *SDKExtension::GetExtensionURL()
{
return SMEXT_CONF_URL;
}
bool SDKExtension::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
return true;
}
void SDKExtension::SDK_OnUnload()
{
}
void SDKExtension::SDK_OnPauseChange(bool paused)
{
}
void SDKExtension::SDK_OnAllLoaded()
{
}
#if defined SMEXT_CONF_METAMOD
PluginId g_PLID = 0; /**< Metamod plugin ID */
ISmmPlugin *g_PLAPI = NULL; /**< Metamod plugin API */
SourceHook::ISourceHook *g_SHPtr = NULL; /**< SourceHook pointer */
ISmmAPI *g_SMAPI = NULL; /**< SourceMM API pointer */
IVEngineServer *engine = NULL; /**< IVEngineServer pointer */
IServerGameDLL *gamedll = NULL; /**< IServerGameDLL pointer */
/** Exposes the extension to Metamod */
SMM_API void *PL_EXPOSURE(const char *name, int *code)
{
#if defined METAMOD_PLAPI_VERSION
if (name && !strcmp(name, METAMOD_PLAPI_NAME))
#else
if (name && !strcmp(name, PLAPI_NAME))
#endif
{
if (code)
{
*code = IFACE_OK;
}
return static_cast<void *>(g_pExtensionIface);
}
if (code)
{
*code = IFACE_FAILED;
}
return NULL;
}
bool SDKExtension::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late)
{
PLUGIN_SAVEVARS();
#if !defined METAMOD_PLAPI_VERSION
GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_CURRENT(engineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
#else
GET_V_IFACE_ANY(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
#endif
m_SourceMMLoaded = true;
return SDK_OnMetamodLoad(ismm, error, maxlen, late);
}
bool SDKExtension::Unload(char *error, size_t maxlen)
{
if (!m_WeAreUnloaded)
{
if (error)
{
snprintf(error, maxlen, "This extension must be unloaded by SourceMod.");
}
return false;
}
return SDK_OnMetamodUnload(error, maxlen);
}
bool SDKExtension::Pause(char *error, size_t maxlen)
{
if (!m_WeGotPauseChange)
{
if (error)
{
snprintf(error, maxlen, "This extension must be paused by SourceMod.");
}
return false;
}
m_WeGotPauseChange = false;
return SDK_OnMetamodPauseChange(true, error, maxlen);
}
bool SDKExtension::Unpause(char *error, size_t maxlen)
{
if (!m_WeGotPauseChange)
{
if (error)
{
snprintf(error, maxlen, "This extension must be unpaused by SourceMod.");
}
return false;
}
m_WeGotPauseChange = false;
return SDK_OnMetamodPauseChange(false, error, maxlen);
}
const char *SDKExtension::GetAuthor()
{
return GetExtensionAuthor();
}
const char *SDKExtension::GetDate()
{
return GetExtensionDateString();
}
const char *SDKExtension::GetDescription()
{
return GetExtensionDescription();
}
const char *SDKExtension::GetLicense()
{
return SMEXT_CONF_LICENSE;
}
const char *SDKExtension::GetLogTag()
{
return GetExtensionTag();
}
const char *SDKExtension::GetName()
{
return GetExtensionName();
}
const char *SDKExtension::GetURL()
{
return GetExtensionURL();
}
const char *SDKExtension::GetVersion()
{
return GetExtensionVerString();
}
bool SDKExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late)
{
return true;
}
bool SDKExtension::SDK_OnMetamodUnload(char *error, size_t maxlength)
{
return true;
}
bool SDKExtension::SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength)
{
return true;
}
#endif
/* Overload a few things to prevent libstdc++ linking */
#if defined __linux__ || defined __APPLE__
extern "C" void __cxa_pure_virtual(void)
{
}
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void *ptr)
{
free(ptr);
}
void operator delete[](void * ptr)
{
free(ptr);
}
void operator delete(void *ptr, size_t size)
{
free(ptr);
}
void operator delete[](void * ptr, size_t size)
{
free(ptr);
}
#endif

View File

@ -1,339 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod Base Extension Code
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_
#define _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_
/**
* @file smsdk_ext.hpp
* @brief Contains wrappers for making Extensions easier to write.
*/
#include "smsdk_config.hpp"
#include <IExtensionSys.h>
#include <IHandleSys.h>
#include <sp_vm_api.h>
#include <sm_platform.h>
#include <ISourceMod.h>
#if defined SMEXT_ENABLE_FORWARDSYS
#include <IForwardSys.h>
#endif //SMEXT_ENABLE_FORWARDSYS
#if defined SMEXT_ENABLE_PLAYERHELPERS
#include <IPlayerHelpers.h>
#endif //SMEXT_ENABLE_PlAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
#include <IDBDriver.h>
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
#include <IGameConfigs.h>
#endif
#if defined SMEXT_ENABLE_MEMUTILS
#include <IMemoryUtils.h>
#endif
#if defined SMEXT_ENABLE_GAMEHELPERS
#include <IGameHelpers.h>
#endif
#if defined SMEXT_ENABLE_TIMERSYS
#include <ITimerSystem.h>
#endif
#if defined SMEXT_ENABLE_ADTFACTORY
#include <IADTFactory.h>
#endif
#if defined SMEXT_ENABLE_THREADER
#include <IThreader.h>
#endif
#if defined SMEXT_ENABLE_LIBSYS
#include <ILibrarySys.h>
#endif
#if defined SMEXT_ENABLE_PLUGINSYS
#include <IPluginSys.h>
#endif
#if defined SMEXT_ENABLE_MENUS
#include <IMenuManager.h>
#endif
#if defined SMEXT_ENABLE_ADMINSYS
#include <IAdminSystem.h>
#endif
#if defined SMEXT_ENABLE_TEXTPARSERS
#include <ITextParsers.h>
#endif
#if defined SMEXT_ENABLE_USERMSGS
#include <IUserMessages.h>
#endif
#if defined SMEXT_ENABLE_TRANSLATOR
#include <ITranslator.h>
#endif
#if defined SMEXT_ENABLE_NINVOKE
#include <INativeInvoker.h>
#endif
#if defined SMEXT_CONF_METAMOD
#include <ISmmPlugin.h>
#include <eiface.h>
#endif
using namespace SourceMod;
using namespace SourcePawn;
class SDKExtension :
#if defined SMEXT_CONF_METAMOD
public ISmmPlugin,
#endif
public IExtensionInterface
{
public:
/** Constructor */
SDKExtension();
public:
/**
* @brief This is called after the initial loading sequence has been processed.
*
* @param error Error message buffer.
* @param maxlength Size of error message buffer.
* @param late Whether or not the module was loaded after map load.
* @return True to succeed loading, false to fail.
*/
virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
/**
* @brief This is called right before the extension is unloaded.
*/
virtual void SDK_OnUnload();
/**
* @brief This is called once all known extensions have been loaded.
*/
virtual void SDK_OnAllLoaded();
/**
* @brief Called when the pause state is changed.
*/
virtual void SDK_OnPauseChange(bool paused);
#if defined SMEXT_CONF_METAMOD
/**
* @brief Called when Metamod is attached, before the extension version is called.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @param late Whether or not Metamod considers this a late load.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
/**
* @brief Called when Metamod is detaching, after the extension version is called.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
/**
* @brief Called when Metamod's pause state is changing.
* NOTE: By default this is blocked unless sent from SourceMod.
*
* @param paused Pause state being set.
* @param error Error buffer.
* @param maxlength Maximum size of error buffer.
* @return True to succeed, false to fail.
*/
virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
#endif
public: //IExtensionInterface
virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late);
virtual void OnExtensionUnload();
virtual void OnExtensionsAllLoaded();
/** Returns whether or not this is a Metamod-based extension */
virtual bool IsMetamodExtension();
/**
* @brief Called when the pause state changes.
*
* @param state True if being paused, false if being unpaused.
*/
virtual void OnExtensionPauseChange(bool state);
/** Returns name */
virtual const char *GetExtensionName();
/** Returns URL */
virtual const char *GetExtensionURL();
/** Returns log tag */
virtual const char *GetExtensionTag();
/** Returns author */
virtual const char *GetExtensionAuthor();
/** Returns version string */
virtual const char *GetExtensionVerString();
/** Returns description string */
virtual const char *GetExtensionDescription();
/** Returns date string */
virtual const char *GetExtensionDateString();
#if defined SMEXT_CONF_METAMOD
public: //ISmmPlugin
/** Called when the extension is attached to Metamod. */
virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late);
/** Returns the author to MM */
virtual const char *GetAuthor();
/** Returns the name to MM */
virtual const char *GetName();
/** Returns the description to MM */
virtual const char *GetDescription();
/** Returns the URL to MM */
virtual const char *GetURL();
/** Returns the license to MM */
virtual const char *GetLicense();
/** Returns the version string to MM */
virtual const char *GetVersion();
/** Returns the date string to MM */
virtual const char *GetDate();
/** Returns the logtag to MM */
virtual const char *GetLogTag();
/** Called on unload */
virtual bool Unload(char *error, size_t maxlength);
/** Called on pause */
virtual bool Pause(char *error, size_t maxlength);
/** Called on unpause */
virtual bool Unpause(char *error, size_t maxlength);
private:
bool m_SourceMMLoaded;
bool m_WeAreUnloaded;
bool m_WeGotPauseChange;
#endif
};
extern SDKExtension *g_pExtensionIface;
extern IExtension *myself;
extern IShareSys *g_pShareSys;
extern IShareSys *sharesys; /* Note: Newer name */
extern ISourceMod *g_pSM;
extern ISourceMod *smutils; /* Note: Newer name */
/* Optional interfaces are below */
#if defined SMEXT_ENABLE_FORWARDSYS
extern IForwardManager *g_pForwards;
extern IForwardManager *forwards; /* Note: Newer name */
#endif //SMEXT_ENABLE_FORWARDSYS
#if defined SMEXT_ENABLE_HANDLESYS
extern IHandleSys *g_pHandleSys;
extern IHandleSys *handlesys; /* Note: Newer name */
#endif //SMEXT_ENABLE_HANDLESYS
#if defined SMEXT_ENABLE_PLAYERHELPERS
extern IPlayerManager *playerhelpers;
#endif //SMEXT_ENABLE_PLAYERHELPERS
#if defined SMEXT_ENABLE_DBMANAGER
extern IDBManager *dbi;
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_GAMECONF
extern IGameConfigManager *gameconfs;
#endif //SMEXT_ENABLE_DBMANAGER
#if defined SMEXT_ENABLE_MEMUTILS
extern IMemoryUtils *memutils;
#endif
#if defined SMEXT_ENABLE_GAMEHELPERS
extern IGameHelpers *gamehelpers;
#endif
#if defined SMEXT_ENABLE_TIMERSYS
extern ITimerSystem *timersys;
#endif
#if defined SMEXT_ENABLE_ADTFACTORY
extern IADTFactory *adtfactory;
#endif
#if defined SMEXT_ENABLE_THREADER
extern IThreader *threader;
#endif
#if defined SMEXT_ENABLE_LIBSYS
extern ILibrarySys *libsys;
#endif
#if defined SMEXT_ENABLE_PLUGINSYS
extern SourceMod::IPluginManager *plsys;
#endif
#if defined SMEXT_ENABLE_MENUS
extern IMenuManager *menus;
#endif
#if defined SMEXT_ENABLE_ADMINSYS
extern IAdminSystem *adminsys;
#endif
#if defined SMEXT_ENABLE_USERMSGS
extern IUserMessages *usermsgs;
#endif
#if defined SMEXT_ENABLE_TRANSLATOR
extern ITranslator *translator;
#endif
#if defined SMEXT_ENABLE_NINVOKE
extern INativeInterface *ninvoke;
#endif
#if defined SMEXT_CONF_METAMOD
PLUGIN_GLOBALVARS();
extern IVEngineServer *engine;
extern IServerGameDLL *gamedll;
#endif
/** Creates a SourceMod interface macro pair */
#define SM_MKIFACE(name) SMINTERFACE_##name##_NAME, SMINTERFACE_##name##_VERSION
/** Automates retrieving SourceMod interfaces */
#define SM_GET_IFACE(prefix, addr) \
if (!g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)) \
{ \
if (error != NULL && maxlength) \
{ \
size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \
if (len >= maxlength) \
{ \
error[maxlength - 1] = '\0'; \
} \
} \
return false; \
}
/** Automates retrieving SourceMod interfaces when needed outside of SDK_OnLoad() */
#define SM_GET_LATE_IFACE(prefix, addr) \
g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)
/** Validates a SourceMod interface pointer */
#define SM_CHECK_IFACE(prefix, addr) \
if (!addr) \
{ \
if (error != NULL && maxlength) \
{ \
size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \
if (len >= maxlength) \
{ \
error[maxlength - 1] = '\0'; \
} \
} \
return false; \
}
#endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_

View File

@ -1 +1 @@
1.2.1
1.3.0