diff --git a/.github/workflows/build-on-release.yml b/.github/workflows/build-on-release.yml index c477f59..a7b9ef9 100644 --- a/.github/workflows/build-on-release.yml +++ b/.github/workflows/build-on-release.yml @@ -16,18 +16,31 @@ jobs: os: - ubuntu-20.04 - windows-latest + target_arch: + - x86 + - x86_64 include: - - meta_branch: "1.11-dev" - sm_branch: "1.11-dev" + - meta_branch: "1.12-dev" + sm_branch: "1.12-dev" spcomp_version: "1.11.x" - os: ubuntu-20.04 + target_arch: x86 os_short: linux - package_ext: tar.gz + + - os: ubuntu-20.04 + target_arch: x86_64 + os_short: linux64 - os: windows-latest + target_arch: x86 os_short: win - package_ext: zip + vs_dev_arch: x86 + + - os: windows-latest + target_arch: x86_64 + os_short: win64 + vs_dev_arch: x64 steps: - name: Prepare env @@ -54,7 +67,7 @@ jobs: run: | :: See https://github.com/microsoft/vswhere/wiki/Find-VC for /f "usebackq delims=*" %%i in (`vswhere -latest -property installationPath`) do ( - call "%%i"\Common7\Tools\vsdevcmd.bat -arch=x86 -host_arch=x64 + call "%%i"\Common7\Tools\vsdevcmd.bat -arch=${{ matrix.vs_dev_arch }} -host_arch=x64 ) :: Loop over all environment variables and make them global. @@ -110,8 +123,41 @@ jobs: run: | mkdir build cd build - python3 ../configure.py --sm-path="${{ github.workspace }}/sourcemod" --mms-path="${{ github.workspace }}/mmsource" --symbol-files --enable-optimize + python3 ../configure.py --sm-path="${{ github.workspace }}/sourcemod" --mms-path="${{ github.workspace }}/mmsource" --symbol-files --enable-optimize --targets ${{ matrix.target_arch }} ambuild + - name: Upload Build Artifact + id: upload-build-artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.os_short }}-${{ matrix.target_arch }} + path: src/build/package + + combine_packages: + name: Upload assets for for ${{ matrix.os_short }} + needs: [build] + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 + - windows-latest + include: + - os: ubuntu-20.04 + os_short: linux + package_ext: .tar.gz + - os: windows-latest + os_short: win + package_ext: zip + + steps: + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + path: src/build/package + pattern: ${{ matrix.os_short }}* + merge-multiple: true - name: Build Package (Windows) if: runner.os == 'Windows' diff --git a/.github/workflows/pull-request-ci-test.yml b/.github/workflows/pull-request-ci-test.yml index c47fdb4..c71a84f 100644 --- a/.github/workflows/pull-request-ci-test.yml +++ b/.github/workflows/pull-request-ci-test.yml @@ -16,8 +16,8 @@ jobs: - ubuntu-22.04 - windows-latest include: - - meta_branch: "1.11-dev" - sm_branch: "1.11-dev" + - meta_branch: "1.12-dev" + sm_branch: "1.12-dev" spcomp_version: "1.11.x" - os: ubuntu-20.04 diff --git a/AMBuildScript b/AMBuildScript index fcf5c7e..4deb6d6 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -1,6 +1,7 @@ # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: -import os -import shutil +import os, sys, shutil + +# Simple extensions do not need to modify this file. def ResolveEnvPath(env, folder): if env in os.environ: @@ -23,13 +24,65 @@ def ResolveEnvPath(env, folder): def Normalize(path): return os.path.abspath(os.path.normpath(path)) -class ExtensionConfig(object): +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 ScriptingMixin: + # helper for building plugins + # call detectSPCompiler prior to running build scripts def __init__(self): - self.extensions = [] + self.spcomp_bin = None self.plugins = {} - self.sm_root = None + + def detectSPCompiler(self): + if builder.options.no_plugins: + return + spcomp_path = builder.options.spcomp_path + if not spcomp_path or not os.path.isdir(spcomp_path): + spcomp_found = shutil.which("spcomp") + if not spcomp_found: + raise Exception('Could not find a path for spcomp') + self.spcomp_bin = shutil.which('spcomp', path = spcomp_path) + if not self.spcomp_bin: + raise Exception('Could not find spcomp') + +class ExtensionConfig(ScriptingMixin, object): + def __init__(self): + super().__init__() + self.binaries = [] + self.extensions = [] + self.generated_headers = None self.mms_root = None - self.spcomp_bin = None + self.sm_root = None + self.all_targets = [] + self.target_archs = set() + + if builder.options.targets: + target_archs = builder.options.targets.split(',') + else: + target_archs = ['x86'] + if builder.backend != 'amb2': + target_archs.append('x86_64') + + 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) + + if not self.all_targets: + raise Exception('No suitable C/C++ compiler was found.') @property def tag(self): @@ -37,53 +90,60 @@ class ExtensionConfig(object): return 'Debug' return 'Release' - def detectSourceMod(self): + def detectSDKs(self): if builder.options.sm_path: self.sm_root = builder.options.sm_path else: - self.sm_root = ResolveEnvPath('SOURCEMOD18', 'sourcemod-1.8') + self.sm_root = ResolveEnvPath('SOURCEMOD112', 'sourcemod-1.12') if not self.sm_root: self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod') if not self.sm_root: self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central') + 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 detectMetaMod(self): if builder.options.mms_path: self.mms_root = builder.options.mms_path else: - self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') + self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12') 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 detectSPCompiler(self): - if builder.options.no_plugins: - return - spcomp_path = builder.options.spcomp_path - if not spcomp_path or not os.path.isdir(spcomp_path): - spcomp_found = shutil.which("spcomp") - if not spcomp_found: - raise Exception('Could not find a path for spcomp') - self.spcomp_bin = shutil.which('spcomp', path = spcomp_path) - if not self.spcomp_bin: - raise Exception('Could not find spcomp') - def configure(self): - cxx = builder.DetectCompilers() + + allowed_archs = ['x86','x86_64'] + + if not set(self.target_archs).issubset(allowed_archs): + 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 < 1914 and builder.options.generator != 'vs': + raise Exception(f'Only MSVC 2017 15.7 and later are supported, full C++17 support is required. ({str(cxx.version)} < 1914)') + elif cxx.family == 'gcc': + if cxx.version < 'gcc-9': + raise Exception('Only GCC versions 9 or later are supported, full C++17 support is required.') + elif cxx.family == 'clang': + if cxx.version < 'clang-5': + raise Exception('Only clang versions 5 or later are supported, full C++17 support is required.') if cxx.like('gcc'): self.configure_gcc(cxx) - elif cxx.vendor == 'msvc': + elif cxx.family == 'msvc': self.configure_msvc(cxx) - # Optimization + # Optimizaiton if builder.options.opt == '1': cxx.defines += ['NDEBUG'] @@ -92,16 +152,19 @@ class ExtensionConfig(object): cxx.defines += ['DEBUG', '_DEBUG'] # Platform-specifics - if builder.target_platform == 'linux': + if cxx.target.platform == 'linux': self.configure_linux(cxx) - elif builder.target_platform == 'mac': + elif cxx.target.platform == 'mac': self.configure_mac(cxx) - elif builder.target_platform == 'windows': + elif cxx.target.platform == 'windows': self.configure_windows(cxx) - # Finish up. + # Arch-specifics; this is copied from hl2sdk-manifests (why??) + if cxx.target.arch == 'x86_64': + cxx.defines += ['X64BITS', 'PLATFORM_64BITS'] + cxx.includes += [ - os.path.join(self.sm_root, 'public'), + os.path.join(builder.sourcePath, 'public'), ] def configure_gcc(self, cxx): @@ -121,32 +184,39 @@ class ExtensionConfig(object): '-Wno-unused', '-Wno-switch', '-Wno-array-bounds', - '-msse', - '-m32', '-fvisibility=hidden', ] + if cxx.target.arch in ['x86', 'x86_64']: + cxx.cflags += ['-msse'] + + cxx.cxxflags += ['-std=c++17'] + cxx.cxxflags += [ - '-std=c++14', - '-fno-exceptions', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', '-Wno-overloaded-virtual', + '-Wno-register', '-fvisibility-inlines-hidden', - '-D_GLIBCXX_USE_CXX11_ABI=0', ] - cxx.linkflags += ['-m32'] - have_gcc = cxx.vendor == 'gcc' - have_clang = cxx.vendor == 'clang' - if cxx.version >= 'clang-3.6': + have_gcc = cxx.family == 'gcc' + have_clang = cxx.family == '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': @@ -155,13 +225,25 @@ class ExtensionConfig(object): cxx.cxxflags += ['-Wno-deprecated'] cxx.cflags += ['-Wno-sometimes-uninitialized'] + # Work around SDK warnings. + if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0': + cxx.cflags += [ + '-Wno-implicit-int-float-conversion', + '-Wno-tautological-overlap-compare', + ] + if have_gcc: cxx.cflags += ['-mfpmath=sse'] + cxx.cflags += ['-Wno-maybe-uninitialized'] if builder.options.opt == '1': - cxx.cflags += ['-O3'] + cxx.cflags += ['-O3'] + + # Don't omit the frame pointer. + cxx.cflags += ['-fno-omit-frame-pointer'] def configure_msvc(self, cxx): + if builder.options.debug == '1': cxx.cflags += ['/MTd'] cxx.linkflags += ['/NODEFAULTLIB:libcmt'] @@ -180,9 +262,9 @@ class ExtensionConfig(object): '/EHsc', '/GR-', '/TP', + '/std:c++17', ] cxx.linkflags += [ - '/MACHINE:X86', 'kernel32.lib', 'user32.lib', 'gdi32.lib', @@ -209,23 +291,23 @@ class ExtensionConfig(object): cxx.cflags += ['/Oy-'] def configure_linux(self, cxx): - cxx.defines += ['_LINUX', 'POSIX'] - cxx.linkflags += ['-Wl,--exclude-libs,ALL', '-lm'] - if cxx.vendor == 'gcc': + cxx.defines += ['LINUX', '_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] + cxx.linkflags += ['-lm'] + if cxx.family == 'gcc': cxx.linkflags += ['-static-libgcc'] - elif cxx.vendor == 'clang': + elif cxx.family == '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.5'] + cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL'] + cxx.cflags += ['-mmacosx-version-min=10.15'] cxx.linkflags += [ - '-mmacosx-version-min=10.5', - '-arch', 'i386', - '-lstdc++', - '-stdlib=libstdc++', + '-mmacosx-version-min=10.15', + '-stdlib=libc++', + '-lc++', ] - cxx.cxxflags += ['-stdlib=libstdc++'] + cxx.cxxflags += ['-stdlib=libc++'] def configure_windows(self, cxx): cxx.defines += ['WIN32', '_WINDOWS'] @@ -233,27 +315,50 @@ class ExtensionConfig(object): 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'), - - os.path.join(self.mms_root, 'core', 'sourcehook'), ] return compiler - def Library(self, context, name): + def ConfigureForHL2(self, binary): + compiler = binary.compiler + SetArchFlags(compiler) + + compiler.cxxincludes += [ + os.path.join(self.mms_root, 'core'), + os.path.join(self.mms_root, 'core', 'sourcehook'), + ] + + compiler.defines += ['META_NO_HL2SDK'] + return binary + + def HL2Library(self, context, name): binary = context.compiler.Library(name) self.ConfigureForExtension(context, binary.compiler) - return binary + return self.ConfigureForHL2(binary) + + def HL2Project(self, context, name): + project = builder.LibraryProject(name) + return project + + def HL2Config(self, context, project, name, compiler): + binary = project.Configure(compiler, name, '{0} - {1}'.format(self.tag, compiler.target.arch)) + self.ConfigureForExtension(context, binary.compiler) + return self.ConfigureForHL2(binary) Extension = ExtensionConfig() -Extension.detectMetaMod() -Extension.detectSourceMod() +Extension.detectSDKs() Extension.detectSPCompiler() Extension.configure() +# This will clone the list and each cxx object as we recurse, preventing child +# scripts from messing up global state. +builder.targets = builder.CloneableList(Extension.all_targets) + # Add additional buildscripts here BuildScripts = [ 'AMBuilder', @@ -269,4 +374,4 @@ if builder.backend == 'amb2': 'PackageScript', ] -builder.RunBuildScripts(BuildScripts, {'Extension': Extension}) +builder.Build(BuildScripts, { 'Extension': Extension }) diff --git a/AMBuilder b/AMBuilder index 49a971d..f9cb947 100644 --- a/AMBuilder +++ b/AMBuilder @@ -16,7 +16,7 @@ sourceFiles = [ # Make sure to edit PackageScript, which copies your files to their appropriate locations # Simple extensions do not need to modify past this point. -project = Extension.Library(builder, projectName + '.ext') +project = Extension.HL2Project(builder, projectName + '.ext') if os.path.isfile(os.path.join(builder.currentSourcePath, 'sdk', 'smsdk_ext.cpp')): # Use the copy included in the project @@ -27,4 +27,7 @@ else: project.sources += sourceFiles -Extension.extensions += [builder.Add(project)] +for cxx in builder.targets: + binary = Extension.HL2Config(builder, project, projectName + '.ext', cxx) + +Extension.extensions += builder.Add(project) diff --git a/PackageScript b/PackageScript index fc7ec98..12a03c6 100644 --- a/PackageScript +++ b/PackageScript @@ -14,6 +14,11 @@ folder_list = [ #'addons/sourcemod/configs', ] +if 'x86_64' in Extension.target_archs: + folder_list.extend([ + 'addons/sourcemod/extensions/x64', + ]) + if Extension.plugins: folder_list += [ 'addons/sourcemod/plugins', @@ -61,7 +66,10 @@ CopyFiles('scripting', 'addons/sourcemod/scripting', # Copy binaries. for cxx_task in Extension.extensions: - builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) + if cxx_task.target.arch == 'x86_64': + builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions/x64']) + else: + builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) # Copy plugins for smx_file, smx_entry in Extension.plugins.items(): diff --git a/configure.py b/configure.py index 81bc874..e99fc6e 100644 --- a/configure.py +++ b/configure.py @@ -4,24 +4,26 @@ # Simple extensions do not need to modify this file. -builder = run.PrepareBuild(sourcePath = sys.path[0]) - -builder.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, - help='Root search folder for HL2SDKs') -builder.options.add_option('--mms-path', type=str, dest='mms_path', default=None, - help='Path to Metamod:Source') -builder.options.add_option('--sm-path', type=str, dest='sm_path', default=None, +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('--hl2sdk-manifest-path', type=str, dest='hl2sdk_manifest', default=None, + help='Path to HL2SDK Manifests') +parser.options.add_argument('--sm-path', type=str, dest='sm_path', default=None, help='Path to SourceMod') -builder.options.add_option('--spcomp-path', type=str, dest='spcomp_path', default=None, - help='Path to directory containing spcomp') -builder.options.add_option('--no-plugins', action='store_true', - help='Do not compile plugins') -builder.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', +parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None, + help='Path to Metamod:Source') + +parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', help='Enable debugging symbols') -builder.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') -builder.options.add_option('-s', '--sdks', default='all', dest='sdks', - help='Build against specified SDKs; valid args are "all", "present", or ' - 'comma-delimited list of engine names (default: %default)') - -builder.Configure() +parser.options.add_argument('-s', '--sdks', default='present', dest='sdks', + help='Build against specified SDKs; valid args are "none", "all", "present",' + ' or comma-delimited list of engine names') +parser.options.add_argument('--targets', type=str, dest='targets', default=None, + help="Override the target architecture (use commas to separate multiple targets).") +parser.options.add_argument('--spcomp-path', type=str, dest='spcomp_path', default=None, + help='Path to directory containing spcomp') +parser.options.add_argument('--no-plugins', action='store_true', help='Do not compile plugins') +parser.Configure() diff --git a/native_utils.cpp b/native_utils.cpp index e97adeb..ba17e7e 100644 --- a/native_utils.cpp +++ b/native_utils.cpp @@ -1,17 +1,26 @@ #include "extension.h" cell_t sm_CellRefToAddress(IPluginContext *pContext, const cell_t *params) { +#ifndef PLATFORM_64BITS cell_t* value; if (pContext->LocalToPhysAddr(params[1], &value) != SP_ERROR_NONE) { return pContext->ThrowNativeError("Failed to convert cell reference to address."); } return reinterpret_cast(value); +#else + return pContext->ThrowNativeError("GetAddressOfCell is not implemented for 64-bit platforms"); +#endif + } cell_t sm_CharArrayToAddress(IPluginContext *pContext, const cell_t *params) { +#ifndef PLATFORM_64BITS char* buffer; if (pContext->LocalToString(params[1], &buffer) != SP_ERROR_NONE) { return pContext->ThrowNativeError("Failed to convert string reference to address."); } return reinterpret_cast(buffer); +#else + return pContext->ThrowNativeError("GetAddressOfString is not implemented for 64-bit platforms"); +#endif } diff --git a/scripting/AMBuilder b/scripting/AMBuilder index c910c44..22c8585 100644 --- a/scripting/AMBuilder +++ b/scripting/AMBuilder @@ -30,9 +30,7 @@ def build_plugin(script_path, smx_file): ) # ??? - (smx_command, smx_outputs) = result - out, *_ = smx_outputs - Extension.plugins[smx_file] = out + Extension.plugins[smx_file], *_ = result for script_file in pluginFiles: script_path = os.path.join(builder.currentSourcePath, script_file) diff --git a/scripting/include/sourcescramble.inc b/scripting/include/sourcescramble.inc index 45e1294..e5df63c 100644 --- a/scripting/include/sourcescramble.inc +++ b/scripting/include/sourcescramble.inc @@ -35,6 +35,9 @@ methodmap MemoryPatch < Handle { /** * Returns the starting address of the patch, equivalent to the address of the patch's * dependent signature plus the patch offset. + * + * @error Plugin is running on a 64-bit server; SourceMod does not support full usage of + * 64-bit addresses at this time. */ property Address Address { public native get(); @@ -59,6 +62,9 @@ methodmap MemoryBlock < Handle { /** * Returns the address of the allocated memory block. + * + * @error Plugin is running on a 64-bit server; SourceMod does not support full usage of + * 64-bit addresses at this time. */ property Address Address { public native get(); @@ -75,44 +81,28 @@ methodmap MemoryBlock < Handle { * Load up to 4 bytes from an offset to the memory block, performing bounds checks to ensure * reads are contained within the block. */ - public int LoadFromOffset(int offset, NumberType size) { - if (offset < 0 || offset + GetNumberTypeByteSize(size) > this.Size) { - ThrowError("Cannot perform MemoryBlock access of %d bytes at offset %d (limit %d)", - GetNumberTypeByteSize(size), offset, this.Size); - } - return LoadFromAddress(this.Address + view_as
(offset), size); - } + public native int LoadFromOffset(int offset, NumberType size); /** * Store up to 4 bytes to an offset to the memory block, performing bounds checks to ensure * writes are contained within the block. */ - public void StoreToOffset(int offset, int data, NumberType size) { - if (offset < 0 || offset + GetNumberTypeByteSize(size) > this.Size) { - ThrowError("Cannot perform MemoryBlock access of %d bytes at offset %d (limit %d)", - GetNumberTypeByteSize(size), offset, this.Size); - } - StoreToAddress(this.Address + view_as
(offset), data, size); - } + public native void StoreToOffset(int offset, int data, NumberType size); }; -static stock int GetNumberTypeByteSize(NumberType size) { - switch (size) { - case NumberType_Int8: return 1; - case NumberType_Int16: return 2; - case NumberType_Int32: return 4; - } - ThrowError("Unknown NumberType %d. Did the SourceMod developers add some new ones?", size); - return 1; // maybe `return (1 << size);` ? -} - /** * Returns the physical memory address of a given SourcePawn cell reference. + * + * @error Plugin is running on a 64-bit server; SourceMod does not support full usage of + * 64-bit addresses at this time. */ native Address GetAddressOfCell(any& cell); /** * Returns the physical memory address of a given string. + * + * @error Plugin is running on a 64-bit server; SourceMod does not support full usage of + * 64-bit addresses at this time. */ native Address GetAddressOfString(char[] array); diff --git a/scripting/sourcescramble_manager.sp b/scripting/sourcescramble_manager.sp index c9c3ecb..26b433a 100644 --- a/scripting/sourcescramble_manager.sp +++ b/scripting/sourcescramble_manager.sp @@ -65,8 +65,8 @@ public SMCResult PatchMemConfigEntry(SMCParser smc, const char[] key, const char if (!patch.Validate()) { PrintToServer("[sourcescramble] Failed to verify patch \"%s\" from \"%s\"", value, key); } else if (patch.Enable()) { - PrintToServer("[sourcescramble] Enabled patch \"%s\" from \"%s\" at address: 0x%08X", - value, key, patch.Address); + PrintToServer("[sourcescramble] Enabled patch \"%s\" from \"%s\"", + value, key); } return SMCParse_Continue; } diff --git a/types/memblock.cpp b/types/memblock.cpp index f9b127f..b19a89c 100644 --- a/types/memblock.cpp +++ b/types/memblock.cpp @@ -3,11 +3,17 @@ #include +#include +#include + HandleType_t g_MemoryBlockType = 0; struct MemoryBlock { MemoryBlock(size_t size) : size{size}, disowned{false} { this->block = calloc(size, 1); + + SourceHook::SetMemAccess((void*) this->block, size * sizeof(uint8_t), + SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC); } ~MemoryBlock() { @@ -30,6 +36,34 @@ bool MemoryBlockHandler::GetHandleApproxSize(HandleType_t type, void* object, un return true; } +enum NumberType +{ + NumberType_Int8, + NumberType_Int16, + NumberType_Int32 +}; + +bool GetNumberTypeSpan(cell_t numberType, size_t& span) { + switch (numberType) { + case 0: { + span = 1; + break; + } + case 1: { + span = 2; + break; + } + case 2: { + span = 4; + break; + } + default: { + return false; + } + } + return true; +} + HandleError ReadMemoryBlockHandle(Handle_t hndl, MemoryBlock **memoryBlock) { HandleSecurity sec(NULL, myself->GetIdentity()); return g_pHandleSys->ReadHandle(hndl, g_MemoryBlockType, &sec, (void **) memoryBlock); @@ -59,6 +93,7 @@ cell_t sm_MemoryBlockCreate(IPluginContext *pContext, const cell_t *params) { } cell_t sm_MemoryBlockPropAddressGet(IPluginContext *pContext, const cell_t *params) { +#ifndef PLATFORM_64BITS Handle_t hndl = static_cast(params[1]); MemoryBlock *pMemoryBlock; @@ -68,6 +103,74 @@ cell_t sm_MemoryBlockPropAddressGet(IPluginContext *pContext, const cell_t *para } return (uintptr_t) pMemoryBlock->block; +#else + return pContext->ThrowNativeError("MemoryBlock.Address is not implemented for 64-bit platforms"); +#endif +} + +cell_t sm_MemoryBlockLoadCellFromOffset(IPluginContext *pContext, const cell_t *params) { + Handle_t hndl = static_cast(params[1]); + + MemoryBlock *pMemoryBlock; + HandleError err; + if ((err = ReadMemoryBlockHandle(hndl, &pMemoryBlock)) != HandleError_None) { + return pContext->ThrowNativeError("Invalid MemoryBlock handle %x (error %d)", hndl, err); + } + + size_t offset = params[2]; + size_t span; + if (!GetNumberTypeSpan(params[3], span)) { + return pContext->ThrowNativeError("Unknown NumberType %d. Did the SourceMod developers add some new ones?", params[3]); + } else if (offset < 0 || offset + span > pMemoryBlock->size) { + return pContext->ThrowNativeError("Cannot perform MemoryBlock access of %d bytes at offset %d (limit %d)", + span, offset, pMemoryBlock->size); + } + + uintptr_t addr = reinterpret_cast(pMemoryBlock->block) + offset; + switch(params[3]) { + case NumberType_Int8: + return *reinterpret_cast(addr); + case NumberType_Int16: + return *reinterpret_cast(addr); + case NumberType_Int32: + return *reinterpret_cast(addr); + } + // we should never hit this code path + return pContext->ThrowNativeError("Unreachable error or failed to handle spans"); +} + +cell_t sm_MemoryBlockStoreCellToOffset(IPluginContext *pContext, const cell_t *params) { + Handle_t hndl = static_cast(params[1]); + + MemoryBlock *pMemoryBlock; + HandleError err; + if ((err = ReadMemoryBlockHandle(hndl, &pMemoryBlock)) != HandleError_None) { + return pContext->ThrowNativeError("Invalid MemoryBlock handle %x (error %d)", hndl, err); + } + + size_t offset = params[2]; + cell_t value = params[3]; + size_t span; + if (!GetNumberTypeSpan(params[4], span)) { + return pContext->ThrowNativeError("Unknown NumberType %d. Did the SourceMod developers add some new ones?", params[3]); + } else if (offset < 0 || offset + span > pMemoryBlock->size) { + return pContext->ThrowNativeError("Cannot perform MemoryBlock access of %d bytes at offset %d (limit %d)", + span, offset, pMemoryBlock->size); + } + + uintptr_t addr = reinterpret_cast(pMemoryBlock->block) + offset; + switch(params[3]) { + case NumberType_Int8: + *reinterpret_cast(addr) = value; + break; + case NumberType_Int16: + *reinterpret_cast(addr) = value; + break; + case NumberType_Int32: + *reinterpret_cast(addr) = value; + break; + } + return 0; } cell_t sm_MemoryBlockPropSizeGet(IPluginContext *pContext, const cell_t *params) { diff --git a/types/memblock.h b/types/memblock.h index 0813a71..dae8024 100644 --- a/types/memblock.h +++ b/types/memblock.h @@ -10,6 +10,9 @@ extern HandleType_t g_MemoryBlockType; cell_t sm_MemoryBlockCreate(IPluginContext *pContext, const cell_t *params); +cell_t sm_MemoryBlockLoadCellFromOffset(IPluginContext *pContext, const cell_t *params); +cell_t sm_MemoryBlockStoreCellToOffset(IPluginContext *pContext, const cell_t *params); + cell_t sm_MemoryBlockPropAddressGet(IPluginContext *pContext, const cell_t *params); cell_t sm_MemoryBlockPropSizeGet(IPluginContext *pContext, const cell_t *params); cell_t sm_MemoryBlockDisown(IPluginContext *pContext, const cell_t *params); @@ -17,6 +20,9 @@ cell_t sm_MemoryBlockDisown(IPluginContext *pContext, const cell_t *params); const sp_nativeinfo_t g_MemoryBlockNatives[] = { { "MemoryBlock.MemoryBlock", sm_MemoryBlockCreate }, + { "MemoryBlock.LoadFromOffset", sm_MemoryBlockLoadCellFromOffset }, + { "MemoryBlock.StoreToOffset", sm_MemoryBlockStoreCellToOffset }, + { "MemoryBlock.Address.get", sm_MemoryBlockPropAddressGet }, { "MemoryBlock.Size.get", sm_MemoryBlockPropSizeGet }, { "MemoryBlock.Disown", sm_MemoryBlockDisown }, diff --git a/types/mempatch.cpp b/types/mempatch.cpp index 105e77f..1b62ec1 100644 --- a/types/mempatch.cpp +++ b/types/mempatch.cpp @@ -177,6 +177,7 @@ cell_t sm_MemoryPatchDisable(IPluginContext *pContext, const cell_t *params) { } cell_t sm_MemoryPatchPropAddressGet(IPluginContext *pContext, const cell_t *params) { +#ifndef PLATFORM_64BITS Handle_t hndl = static_cast(params[1]); MemoryPatch *pMemoryPatch; @@ -186,4 +187,7 @@ cell_t sm_MemoryPatchPropAddressGet(IPluginContext *pContext, const cell_t *para } return pMemoryPatch->pAddress; +#else + return pContext->ThrowNativeError("MemoryPatch.Address is not implemented for 64-bit platforms"); +#endif } \ No newline at end of file diff --git a/userconf/mempatches.cpp b/userconf/mempatches.cpp index dc1285d..a9d2d2a 100644 --- a/userconf/mempatches.cpp +++ b/userconf/mempatches.cpp @@ -38,9 +38,19 @@ ByteVector ByteVectorFromString(const char* s) { */ static inline bool IsTargetPlatformSection(const char* name) { #if defined WIN32 +#if defined PLATFORM_64BITS + return !strcmp(name, "windows64"); +#else return !strcmp(name, "windows"); +#endif // PLATFORM_64BITS + #elif defined _LINUX +#if defined PLATFORM_64BITS + return !strcmp(name, "linux64"); +#else return !strcmp(name, "linux"); +#endif // PLATFORM_64BITS + #elif defined _OSX return !strcmp(name, "mac"); #endif @@ -50,13 +60,17 @@ static inline bool IsTargetPlatformSection(const char* name) { * Return true if the name is for an operating system but not the current one. */ static inline bool IsNonTargetPlatformSection(const char* name) { -#if defined WIN32 - return (!strcmp(name, "linux") || !strcmp(name, "mac")); -#elif defined _LINUX - return (!strcmp(name, "windows") || !strcmp(name, "mac")); -#elif defined _OSX - return (!strcmp(name, "windows") || !strcmp(name, "linux")); -#endif + if (IsTargetPlatformSection(name)) { + return false; + } + /* mutually exclusive with the above */ + return ( + !strcmp(name, "windows") + || !strcmp(name, "windows64") + || !strcmp(name, "linux") + || !strcmp(name, "linux64") + || !strcmp(name, "mac") + ); } /**