From 33a5f066e7c8adee2f7fa643ebac4c1c4d678d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=87=E5=AE=99=E9=81=A8=E6=B8=B8?= <2746069727@qq.com> Date: Fri, 20 Oct 2023 07:34:41 +0800 Subject: [PATCH] upload --- AMBuildScript | 536 ++++++++++++++++++ AMBuilder | 42 ++ PackageScript | 37 ++ Skin.cpp | 336 +++++++++++ Skin.h | 67 +++ build.bat | 3 + build.sh | 3 + configure.py | 39 ++ sdk/CBaseAnimGraph.h | 8 + sdk/CBaseCSGrenadeProjectile.h | 7 + sdk/CBaseCombatCharacter.h | 8 + sdk/CBaseEntity.h | 29 + sdk/CBaseFlex.h | 8 + sdk/CBaseGrenade.h | 16 + sdk/CBaseModelEntity.h | 8 + sdk/CBasePlayerController.h | 13 + sdk/CBasePlayerPawn.h | 14 + sdk/CCSPlayerController.h | 15 + sdk/CCSPlayerController_InGameMoneyServices.h | 9 + sdk/CCSPlayerPawn.h | 8 + sdk/CCSPlayerPawnBase.h | 9 + sdk/CCSPlayer_ItemServices.h | 33 ++ sdk/CGameRules.h | 43 ++ sdk/CGameRulesProxy.h | 15 + sdk/CPlayerPawnComponent.h | 8 + sdk/CPlayer_ItemServices.h | 66 +++ sdk/CSmokeGrenadeProjectile.h | 10 + sdk/schemasystem.cpp | 44 ++ sdk/schemasystem.h | 93 +++ steamid.py | 224 ++++++++ utils.hpp | 14 + utils/memaddr.cpp | 66 +++ utils/memaddr.h | 117 ++++ utils/module.cpp | 279 +++++++++ utils/module.h | 50 ++ 35 files changed, 2277 insertions(+) create mode 100644 AMBuildScript create mode 100644 AMBuilder create mode 100644 PackageScript create mode 100644 Skin.cpp create mode 100644 Skin.h create mode 100644 build.bat create mode 100644 build.sh create mode 100644 configure.py create mode 100644 sdk/CBaseAnimGraph.h create mode 100644 sdk/CBaseCSGrenadeProjectile.h create mode 100644 sdk/CBaseCombatCharacter.h create mode 100644 sdk/CBaseEntity.h create mode 100644 sdk/CBaseFlex.h create mode 100644 sdk/CBaseGrenade.h create mode 100644 sdk/CBaseModelEntity.h create mode 100644 sdk/CBasePlayerController.h create mode 100644 sdk/CBasePlayerPawn.h create mode 100644 sdk/CCSPlayerController.h create mode 100644 sdk/CCSPlayerController_InGameMoneyServices.h create mode 100644 sdk/CCSPlayerPawn.h create mode 100644 sdk/CCSPlayerPawnBase.h create mode 100644 sdk/CCSPlayer_ItemServices.h create mode 100644 sdk/CGameRules.h create mode 100644 sdk/CGameRulesProxy.h create mode 100644 sdk/CPlayerPawnComponent.h create mode 100644 sdk/CPlayer_ItemServices.h create mode 100644 sdk/CSmokeGrenadeProjectile.h create mode 100644 sdk/schemasystem.cpp create mode 100644 sdk/schemasystem.h create mode 100644 steamid.py create mode 100644 utils.hpp create mode 100644 utils/memaddr.cpp create mode 100644 utils/memaddr.h create mode 100644 utils/module.cpp create mode 100644 utils/module.h diff --git a/AMBuildScript b/AMBuildScript new file mode 100644 index 0000000..651fed3 --- /dev/null +++ b/AMBuildScript @@ -0,0 +1,536 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os, sys + +additional_libs = [ + # Path should be relative either to hl2sdk folder or to build folder + #'path/to/lib/example.lib', +] + +additional_defines = [ + #'EXAMPLE_DEFINE=2' +] + +additional_includes = [ + # Path should be absolute only! + #'D:/absolute/path/to/include/folder/' +] + +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.name = dir + self.path = None # Actual path + self.platformSpec = platform + + # By default, nothing supports x64. + if type(platform) is list: + self.platformSpec = {p: ['x86'] for p in platform} + else: + self.platformSpec = platform + + 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 + +WinOnly = ['windows'] +WinLinux = ['windows', 'linux'] +WinLinuxMac = ['windows', 'linux', 'mac'] +CSGO = { + 'windows': ['x86'], + 'linux': ['x86', 'x86_64'], + 'mac': ['x86_64'] +} +Source2 = { + 'windows': ['x86_64'], + 'linux': ['x86_64'], +} +Insurgency = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86'], + 'mac': ['x86', 'x86_64'], +} +Blade = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86_64'] +} +Mock = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86', 'x86_64'], + 'mac': ['x86_64'] +} + +PossibleSDKs = { + 'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'), + 'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'), + 'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'), + 'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'), + 'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'), + 'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'), + 'tf2': SDK('HL2SDKTF2', '2.tf2', '12', 'TF2', WinLinuxMac, 'tf2'), + 'l4d': SDK('HL2SDKL4D', '2.l4d', '13', 'LEFT4DEAD', WinLinuxMac, 'l4d'), + 'nucleardawn': SDK('HL2SDKND', '2.nd', '14', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'), + 'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '16', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'), + 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'), + 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '17', 'ALIENSWARM', WinOnly, 'swarm'), + 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), + 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), + 'mcv': SDK('HL2SDKMCV', '2.mcv', '22', 'MCV', WinOnly, 'mcv'), + 'csgo': SDK('HL2SDKCSGO', '2.csgo', '23', 'CSGO', CSGO, 'csgo'), + 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '18', 'PORTAL2', [], 'portal2'), + 'blade': SDK('HL2SDKBLADE', '2.blade', '19', 'BLADE', Blade, 'blade'), + 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '20', 'INSURGENCY', Insurgency, 'insurgency'), + 'doi': SDK('HL2SDKDOI', '2.doi', '21', 'DOI', WinLinuxMac, 'doi'), + 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '15', 'CONTAGION', WinOnly, 'contagion'), + 'bms': SDK('HL2SDKBMS', '2.bms', '11', 'BMS', WinLinux, 'bms'), + 'mock': SDK('HL2SDK-MOCK', '2.mock', '999', 'MOCK', Mock, 'mock'), + 'pvkii': SDK('HL2SDKPVKII', '2.pvkii', '10', 'PVKII', WinLinux, 'pvkii'), + 'dota': SDK('HL2SDKDOTA', '2.dota', '24', 'DOTA', Source2, 'dota'), + 'cs2': SDK('HL2SDKCS2', '2.cs2', '25', 'CS2', Source2, 'cs2'), +} + +def ResolveEnvPath(env, folder): + if env in os.environ: + path = os.environ[env] + if os.path.isdir(path): + return path + else: + head = os.getcwd() + oldhead = None + while head != None and head != oldhead: + path = os.path.join(head, folder) + if os.path.isdir(path): + return path + oldhead = head + head, tail = os.path.split(head) + return None + +def Normalize(path): + return os.path.abspath(os.path.normpath(path)) + +class MMSPluginConfig(object): + def __init__(self): + self.sdks = {} + self.binaries = [] + self.mms_root = None + self.all_targets = [] + self.target_archs = set() + + if builder.options.plugin_name is not None: + self.plugin_name = builder.options.plugin_name + else: + self.plugin_name = 'Skin' + + if builder.options.plugin_alias is not None: + self.plugin_alias = builder.options.plugin_alias + else: + self.plugin_alias = 'Skin' + + if builder.options.targets: + target_archs = builder.options.targets.split(',') + else: + target_archs = ['x86'] + 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.') + + def detectSDKs(self): + sdk_list = builder.options.sdks.split(',') + use_all = sdk_list[0] == 'all' + use_present = sdk_list[0] == 'present' + if sdk_list[0] == '': + sdk_list = [] + + not_found = [] + 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(builder.options.hl2sdk_root, sdk.folder) + if not os.path.exists(sdk_path): + sdk_path = None + else: + sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder) + if sdk_path is None: + if (use_all and sdk_name != 'mock') or sdk_name in sdk_list: + raise Exception('Could not find a valid path for {0}'.format(sdk.envvar)) + not_found.append(sdk_name) + continue + if use_all or use_present or sdk_name in sdk_list: + sdk.path = sdk_path + self.sdks[sdk_name] = sdk + + if len(self.sdks) < 1 and len(sdk_list): + raise Exception('No SDKs were found, nothing to build.') + + if len(self.sdks) > 1: + raise Exception('Only one sdk at a time is supported, for multi-sdk approach use loader based solution.') + + if builder.options.mms_path: + self.mms_root = builder.options.mms_path + else: + self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12') + if not self.mms_root: + self.mms_root = ResolveEnvPath('MMSOURCE111', 'mmsource-1.11') + if not self.mms_root: + self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') + if not self.mms_root: + self.mms_root = ResolveEnvPath('MMSOURCE_DEV', '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) + + if use_present: + for sdk in not_found: + print('Warning: hl2sdk-{} was not found, and will not be included in build.'.format(sdk)) + + def configure(self): + for cxx in self.all_targets: + if cxx.target.arch not in ['x86', 'x86_64']: + raise Exception('Unknown target architecture: {0}'.format(arch)) + + self.configure_cxx(cxx) + + def configure_cxx(self, cxx): + if cxx.behavior == 'gcc': + cxx.defines += [ + 'stricmp=strcasecmp', + '_stricmp=strcasecmp', + '_snprintf=snprintf', + '_vsnprintf=vsnprintf', + 'HAVE_STDINT_H', + 'GNUC', + ] + cxx.cflags += [ + '-pipe', + '-fno-strict-aliasing', + '-Wall', + '-Werror', + '-Wignored-attributes', + '-Wno-uninitialized', + '-Wno-unused', + '-Wno-switch', + '-Wno-register', + '-Wno-macro-redefined', + '-Wno-mismatched-tags', + '-msse', + '-fPIC', + ] + + if cxx.version == 'apple-clang-6.0' or cxx.version == 'clang-3.4': + cxx.cxxflags += ['-std=c++1y'] + else: + cxx.cxxflags += ['-std=c++17'] + if (cxx.version >= 'gcc-4.0') or cxx.family == 'clang': + cxx.cflags += ['-fvisibility=hidden'] + cxx.cxxflags += ['-fvisibility-inlines-hidden'] + cxx.cxxflags += [ + '-fno-exceptions', + '-fno-threadsafe-statics', + '-Wno-non-virtual-dtor', + '-Wno-overloaded-virtual', + ] + if (cxx.version >= 'gcc-4.7' or cxx.family == 'clang'): + cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] + if cxx.family == 'gcc': + cxx.cflags += ['-mfpmath=sse'] + if cxx.family == 'clang': + cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch'] + if cxx.version >= 'clang-3.9' or cxx.version >= 'apple-clang-10.0': + cxx.cxxflags += ['-Wno-expansion-to-defined'] + if cxx.version >= 'clang-3.6' or cxx.version >= 'apple-clang-7.0': + cxx.cxxflags += ['-Wno-inconsistent-missing-override'] + if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4': + cxx.cxxflags += ['-Wno-deprecated-register'] + else: + cxx.cxxflags += ['-Wno-deprecated'] + + # 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', + '-Wignored-attributes', + '-Wmissing-declarations' + ] + + elif cxx.like('msvc'): + 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', + ] + cxx.cflags += [ + '/W3', + '/Zi', + ] + cxx.cxxflags += [ + '/TP', + '/std:c++17', + ] + + cxx.linkflags += [ + '/SUBSYSTEM:WINDOWS', + 'kernel32.lib', + 'user32.lib', + 'gdi32.lib', + 'winspool.lib', + 'comdlg32.lib', + 'advapi32.lib', + 'shell32.lib', + 'ole32.lib', + 'oleaut32.lib', + 'uuid.lib', + 'odbc32.lib', + 'odbccp32.lib', + ] + + # Optimization + if builder.options.opt == '1': + cxx.defines += ['NDEBUG'] + if cxx.behavior == 'gcc': + cxx.cflags += ['-O3'] + elif cxx.behavior == 'msvc': + cxx.cflags += ['/Ox', '/Zo'] + cxx.linkflags += ['/OPT:ICF', '/OPT:REF'] + + # Debugging + if builder.options.debug == '1': + cxx.defines += ['DEBUG', '_DEBUG'] + if cxx.behavior == 'gcc': + cxx.cflags += ['-g3'] + elif cxx.behavior == 'msvc': + cxx.cflags += ['/Od', '/RTC1'] + + # Don't omit the frame pointer. + # This needs to be after our optimization flags which could otherwise disable it. + if cxx.behavior == 'gcc': + cxx.cflags += ['-fno-omit-frame-pointer'] + elif cxx.behavior == 'msvc': + cxx.cflags += ['/Oy-'] + + # Platform-specifics + if cxx.target.platform == 'linux': + cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] + if cxx.family == 'gcc': + cxx.linkflags += ['-static-libgcc'] + elif cxx.family == 'clang': + cxx.linkflags += ['-lgcc_eh'] + elif cxx.target.platform == 'mac': + cxx.defines += ['OSX', '_OSX', 'POSIX'] + + if cxx.version >= 'apple-clang-10.0': + cxx.cflags += ['-mmacosx-version-min=10.9', '-stdlib=libc++'] + cxx.linkflags += [ + '-mmacosx-version-min=10.9', + ] + else: + cxx.cflags += ['-mmacosx-version-min=10.5'] + cxx.linkflags += [ + '-mmacosx-version-min=10.5', + ] + + cxx.linkflags += [ + '-lc++', + ] + elif cxx.target.platform == 'windows': + cxx.defines += ['WIN32', '_WINDOWS'] + + # Finish up. + # Custom defines here + cxx.defines += [ ] + # Custom includes here + cxx.includes += [ ] + + def HL2Compiler(self, context, cxx, sdk): + compiler = cxx.clone() + mms_core_path = os.path.join(self.mms_root, 'core') + compiler.cxxincludes += [ + os.path.join(mms_core_path), + os.path.join(mms_core_path, 'sourcehook'), + os.path.join(context.currentSourcePath), + ] + + 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: + paths.append(['public', 'game', 'server']) + paths.append(['game', 'shared']) + paths.append(['common']) + paths.append(['public', 'entity2']) + paths.append(['game', 'server']) + compiler.defines += ['SOURCE_ENGINE=' + sdk.code] + + if sdk.name in ['sdk2013', 'bms', 'pvkii'] 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 compiler.family == 'msvc': + compiler.defines += ['COMPILER_MSVC'] + if compiler.target.arch == 'x86': + compiler.defines += ['COMPILER_MSVC32'] + elif compiler.target.arch == 'x86_64': + compiler.defines += ['COMPILER_MSVC64'] + + if compiler.version >= 1900: + compiler.linkflags += ['legacy_stdio_definitions.lib'] + else: + compiler.defines += ['COMPILER_GCC'] + + if compiler.target.arch == 'x86_64': + compiler.defines += ['X64BITS', 'PLATFORM_64BITS'] + + if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota', 'cs2', 'pvkii']: + if compiler.target.platform in ['linux', 'mac']: + compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] + + if sdk.name in ['csgo', 'blade', 'pvkii'] and compiler.target.platform == 'linux': + compiler.linkflags += ['-lstdc++'] + + if sdk.name in ['dota', 'cs2']: + compiler.defines += ['META_IS_SOURCE2'] + + for path in paths: + compiler.cxxincludes += [os.path.join(sdk.path, *path)] + + compiler.linkflags += additional_libs + compiler.defines += additional_defines + compiler.cxxincludes += additional_includes + + return compiler + + def Library(self, cxx, name): + binary = cxx.Library(name) + return binary + + def HL2Library(self, context, compiler, name, sdk): + compiler = self.HL2Compiler(context, compiler, sdk) + + if compiler.target.platform == 'linux': + if sdk.name == 'episode1': + lib_folder = os.path.join(sdk.path, 'linux_sdk') + elif sdk.name in ['sdk2013', 'bms', 'pvkii']: + 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: + 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: + lib_folder = os.path.join(sdk.path, 'lib', 'mac') + + if compiler.target.platform in ['linux', 'mac']: + if sdk.name in ['sdk2013', 'bms', 'pvkii'] or compiler.target.arch == 'x86_64': + tier1 = os.path.join(lib_folder, 'tier1.a') + else: + tier1 = os.path.join(lib_folder, 'tier1_i486.a') + if sdk.name == 'mock' and compiler.target.platform == 'linux': + compiler.linkflags += ['-Wl,-z,origin'] + compiler.postlink += [tier1] + + if sdk.name in ['blade', 'insurgency', 'doi', 'csgo', 'cs2', 'dota']: + 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')] + + if sdk.name == 'bms': + compiler.postlink += [os.path.join(lib_folder, 'mathlib.a')] + + binary = self.Library(compiler, name) + compiler = binary.compiler + + dynamic_libs = [] + if compiler.target.platform == 'linux': + compiler.linkflags[0:0] = ['-lm'] + 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', 'cs2', 'dota', 'pvkii']: + dynamic_libs = ['libtier0.so'] + if sdk.name not in ['dota', 'cs2']: + dynamic_libs += ['libvstdlib.so'] + else: + dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so'] + if sdk.name in ['csgo', 'blade']: + compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0'] + elif compiler.target.platform == 'mac': + binary.compiler.linkflags.append('-liconv') + dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib'] + elif compiler.target.platform == 'windows': + libs = ['tier0', 'tier1', 'mathlib'] + if sdk.name not in ['dota', 'cs2']: + libs += ['vstdlib'] + if sdk.name in ['swarm', 'blade', 'insurgency', 'doi', 'mcv', 'csgo', 'cs2', 'dota']: + 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' + binary.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) + + context.AddFolder(binary.localFolder) + output = context.AddSymlink(source_path, output_path) + + binary.compiler.weaklinkdeps += [output] + binary.compiler.linkflags[0:0] = [library] + + return binary + +MMSPlugin = MMSPluginConfig() +MMSPlugin.detectSDKs() +MMSPlugin.configure() + +BuildScripts = [ + 'AMBuilder', + 'PackageScript', +] + +builder.Build(BuildScripts, { 'MMSPlugin': MMSPlugin }) diff --git a/AMBuilder b/AMBuilder new file mode 100644 index 0000000..30a8eca --- /dev/null +++ b/AMBuilder @@ -0,0 +1,42 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os + +# Here only one sdk should be available to generate only one executable in the end, +# as multi-sdk loading isn't supported out of the box by metamod, and would require specifying the full path in the vdf +# which in the end would ruin the multi-platform (unix, win etc) loading by metamod as it won't be able to append platform specific extension +# so just fall back to the single binary. +# Multi-sdk solutions should be manually loaded with a custom plugin loader (examples being sourcemod, stripper:source) +for sdk_name in MMSPlugin.sdks: + for cxx in MMSPlugin.all_targets: + sdk = MMSPlugin.sdks[sdk_name] + + if not cxx.target.arch in sdk.platformSpec[cxx.target.platform]: + continue + + binary = MMSPlugin.HL2Library(builder, cxx, MMSPlugin.plugin_name, sdk) + + if binary.compiler.family == 'gcc' or binary.compiler.family == 'clang': + binary.compiler.cxxflags += ['-Wno-register'] + binary.compiler.cxxflags += ['--std=c++17'] + + binary.sources += [ + 'Skin.cpp', + 'utils/module.cpp', + 'utils/memaddr.cpp', + os.path.join('sdk', 'schemasystem.cpp') + ] + + if sdk_name in ['dota', 'cs2']: + binary.sources += [ + os.path.join(sdk.path, 'tier1', 'convar.cpp'), + os.path.join(sdk.path, 'tier1', 'generichash.cpp'), + os.path.join(sdk.path, 'entity2', 'entitysystem.cpp'), + os.path.join(sdk.path, 'public', 'tier0', 'memoverride.cpp') + ] + + if cxx.target.arch == 'x86': + binary.sources += ['sourcehook/sourcehook_hookmangen.cpp'] + nodes = builder.Add(binary) + MMSPlugin.binaries += [nodes] + + break diff --git a/PackageScript b/PackageScript new file mode 100644 index 0000000..27bacf1 --- /dev/null +++ b/PackageScript @@ -0,0 +1,37 @@ +# vim: set ts=2 sw=2 tw=99 noet ft=python: +import os + +builder.SetBuildFolder('package') + +metamod_folder = builder.AddFolder(os.path.join('addons', 'metamod')) +bin_folder_path = os.path.join('addons', MMSPlugin.plugin_name) +bin_folder = builder.AddFolder(bin_folder_path) + +for cxx in MMSPlugin.all_targets: + if cxx.target.arch == 'x86_64': + bin64_folder_path = os.path.join('addons', MMSPlugin.plugin_name) + bin64_folder = builder.AddFolder(bin64_folder_path) + +pdb_list = [] +for task in MMSPlugin.binaries: + # This hardly assumes there's only 1 targetted platform and would be overwritten + # with whatever comes last if multiple are used! + with open(os.path.join(builder.buildPath, MMSPlugin.plugin_name + '.vdf'), 'w') as fp: + fp.write('"Metamod Plugin"\n') + fp.write('{\n') + fp.write(f'\t"alias"\t"{MMSPlugin.plugin_alias}"\n') + if task.target.arch == 'x86_64': + fp.write(f'\t"file"\t"{os.path.join(bin64_folder_path, MMSPlugin.plugin_name)}"\n') + else: + fp.write(f'\t"file"\t"{os.path.join(bin_folder_path, MMSPlugin.plugin_name)}"\n') + fp.write('}\n') + + if task.target.arch == 'x86_64': + builder.AddCopy(task.binary, bin64_folder) + else: + builder.AddCopy(task.binary, bin_folder) + + if task.debug: + pdb_list.append(task.debug) + +builder.AddCopy(os.path.join(builder.buildPath, MMSPlugin.plugin_name + '.vdf'), metamod_folder) \ No newline at end of file diff --git a/Skin.cpp b/Skin.cpp new file mode 100644 index 0000000..f6a5562 --- /dev/null +++ b/Skin.cpp @@ -0,0 +1,336 @@ +#include +#include "Skin.h" +#include "metamod_oslink.h" +#include "utils.hpp" +#include +#include +#include "sdk/schemasystem.h" +#include "sdk/CBaseEntity.h" +#include "sdk/CGameRulesProxy.h" +#include "sdk/CBasePlayerPawn.h" +#include "sdk/CCSPlayerController.h" +#include "sdk/CCSPlayer_ItemServices.h" +#include "sdk/CSmokeGrenadeProjectile.h" +#include +#ifdef _WIN32 +#include +#include +#else +#include "utils/module.h" +#endif +#include + +Skin g_Skin; +PLUGIN_EXPOSE(Skin, g_Skin); +IVEngineServer2* engine = nullptr; +IGameEventManager2* gameeventmanager = nullptr; +IGameResourceServiceServer* g_pGameResourceService = nullptr; +CGameEntitySystem* g_pGameEntitySystem = nullptr; +CEntitySystem* g_pEntitySystem = nullptr; +CSchemaSystem* g_pCSchemaSystem = nullptr; +CCSGameRules* g_pGameRules = nullptr; +CPlayerSpawnEvent g_PlayerSpawnEvent; +CRoundPreStartEvent g_RoundPreStartEvent; +CEntityListener g_EntityListener; +bool g_bPistolRound; + +typedef struct SkinParm +{ + int m_nFallbackPaintKit; + int m_nFallbackSeed; + float m_flFallbackWear; +}SkinParm;; + +#ifdef _WIN32 +typedef void*(FASTCALL* EntityRemove_t)(CGameEntitySystem*, void*, void*,uint64_t); +typedef void(FASTCALL* GiveNamedItem_t)(void* itemService,const char* pchName, void* iSubType,void* pScriptItem, void* a5,void* a6); +typedef void(FASTCALL* UTIL_ClientPrintAll_t)(int msg_dest, const char* msg_name, const char* param1, const char* param2, const char* param3, const char* param4); + +extern EntityRemove_t FnEntityRemove; +extern GiveNamedItem_t FnGiveNamedItem; +extern UTIL_ClientPrintAll_t FnUTIL_ClientPrintAll; +EntityRemove_t FnEntityRemove; +GiveNamedItem_t FnGiveNamedItem; +UTIL_ClientPrintAll_t FnUTIL_ClientPrintAll; +#else +void (*FnEntityRemove)(CGameEntitySystem*, void*, void*,uint64_t) = nullptr; +void (*FnGiveNamedItem)(void* itemService,const char* pchName, void* iSubType,void* pScriptItem, void* a5,void* a6) = nullptr; +void (*FnUTIL_ClientPrintAll)(int msg_dest, const char* msg_name, const char* param1, const char* param2, const char* param3, const char* param4) = nullptr; +#endif + +std::map g_WeaponsMap; +std::map> g_PlayerSkins; + +class GameSessionConfiguration_t { }; +SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t&, ISource2WorldSession*, const char*); +SH_DECL_HOOK3_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool, bool, bool); + +#ifdef _WIN32 +inline void* FindSignature(const char* modname,const char* sig) +{ + DWORD64 hModule = (DWORD64)GetModuleHandle(modname); + if (!hModule) + { + return NULL; + } + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); + MODULEENTRY32 mod = {sizeof(MODULEENTRY32)}; + + while (Module32Next(hSnap, &mod)) + { + if (!strcmp(modname, mod.szModule)) + { + if(!strstr(mod.szExePath,"metamod")) + break; + } + } + CloseHandle(hSnap); + byte* b_sig = (byte*)sig; + int sig_len = strlen(sig); + byte* addr = (byte*)mod.modBaseAddr; + for (int i = 0; i < mod.modBaseSize; i++) + { + int flag = 0; + for (int n = 0; n < sig_len; n++) + { + if (i + n >= mod.modBaseSize)break; + if (*(b_sig + n)=='\x3F' || *(b_sig + n) == *(addr + i+ n)) + { + flag++; + } + } + if (flag == sig_len) + { + return (void*)(addr + i); + } + } + return NULL; +} +#endif + +bool Skin::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late) +{ + PLUGIN_SAVEVARS(); + + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer2, SOURCE2ENGINETOSERVER_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetServerFactory, g_pSource2Server, ISource2Server, SOURCE2SERVER_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pNetworkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pGameResourceService, IGameResourceServiceServer, GAMERESOURCESERVICESERVER_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION); + + // Get CSchemaSystem + { + HINSTANCE m_hModule = dlmount(WIN_LINUX("schemasystem.dll", "libschemasystem.so")); + g_pCSchemaSystem = reinterpret_cast(reinterpret_cast(dlsym(m_hModule, "CreateInterface"))(SCHEMASYSTEM_INTERFACE_VERSION, nullptr)); + dlclose(m_hModule); + } + + SH_ADD_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &Skin::StartupServer), true); + SH_ADD_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &Skin::GameFrame), true); + + gameeventmanager = static_cast(CallVFunc(g_pSource2Server)); + + ConVar_Register(FCVAR_GAMEDLL); + + g_WeaponsMap = { {26,"weapon_bizon"},{27,"weapon_mac10"},{34,"weapon_mp9"},{19,"weapon_p90"},{24,"weapon_ump45"},{7,"weapon_ak47"},{8,"weapon_aug"},{10,"weapon_famas"},{13,"weapon_galilar"},{16,"weapon_m4a1"},{60,"weapon_m4a1_silencer"},{39,"weapon_sg556"},{9,"weapon_awp"},{11,"weapon_g3sg1"},{38,"weapon_scar20"},{40,"weapon_ssg08"},{29,"weapon_mag7"},{35,"weapon_nova"},{29,"weapon_sawedoff"},{25,"weapon_xm1014"},{14,"weapon_m249"},{9,"weapon_awp"},{28,"weapon_negev"},{1,"weapon_deagle"},{2,"weapon_elite"},{3,"weapon_fiveseven"},{4,"weapon_glock"},{32,"weapon_hkp2000"},{36,"weapon_p250"},{30,"weapon_tec9"},{61,"weapon_usp_silencer"},{63,"weapon_cz75a"},{64,"weapon_revolver"}}; + #ifdef _WIN32 + byte* vscript = (byte*)FindSignature("vscript.dll", "\xBE\x01\x3F\x3F\x3F\x2B\xD6\x74\x61\x3B\xD6"); + if(vscript) + { + DWORD pOld; + VirtualProtect(vscript, 2, PAGE_EXECUTE_READWRITE, &pOld); + *(vscript + 1) = 2; + VirtualProtect(vscript, 2, pOld, &pOld); + } + #endif + return true; +} + +bool Skin::Unload(char *error, size_t maxlen) +{ + SH_REMOVE_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &Skin::GameFrame), true); + SH_REMOVE_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &Skin::StartupServer), true); + + gameeventmanager->RemoveListener(&g_PlayerSpawnEvent); + gameeventmanager->RemoveListener(&g_RoundPreStartEvent); + + g_pGameEntitySystem->RemoveListenerEntity(&g_EntityListener); + + ConVar_Unregister(); + + return true; +} + +void Skin::NextFrame(std::function fn) +{ + m_nextFrame.push_back(fn); +} + +void Skin::StartupServer(const GameSessionConfiguration_t& config, ISource2WorldSession*, const char*) +{ + #ifdef _WIN32 + FnUTIL_ClientPrintAll = (UTIL_ClientPrintAll_t)FindSignature("server.dll", "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x81\xEC\x70\x01\x3F\x3F\x8B\xE9"); + FnGiveNamedItem = (GiveNamedItem_t)FindSignature("server.dll", "\x48\x89\x5C\x24\x18\x48\x89\x74\x24\x20\x55\x57\x41\x54\x41\x56\x41\x57\x48\x8D\x6C\x24\xD9"); + FnEntityRemove = (EntityRemove_t)FindSignature("server.dll", "\x48\x85\xD2\x0F\x3F\x3F\x3F\x3F\x3F\x57\x48\x3F\x3F\x3F\x48\x89\x3F\x3F\x3F\x48\x8B\xF9\x48\x8B"); + #else + CModule libserver(g_pSource2Server); + FnUTIL_ClientPrintAll = libserver.FindPatternSIMD("55 48 89 E5 41 57 49 89 D7 41 56 49 89 F6 41 55 41 89 FD").RCast< decltype(FnUTIL_ClientPrintAll) >(); + FnGiveNamedItem = libserver.FindPatternSIMD("55 48 89 E5 41 57 41 56 49 89 CE 41 55 49 89 F5 41 54 49 89 D4 53 48 89").RCast(); + FnEntityRemove = libserver.FindPatternSIMD("48 85 F6 74 0B 48 8B 76 10 E9 B2 FE FF FF").RCast(); + #endif + g_pGameRules = nullptr; + + static bool bDone = false; + if (!bDone) + { + g_pGameEntitySystem = *reinterpret_cast(reinterpret_cast(g_pGameResourceService) + WIN_LINUX(0x58, 0x50)); + g_pEntitySystem = g_pGameEntitySystem; + + g_pGameEntitySystem->AddListenerEntity(&g_EntityListener); + + gameeventmanager->AddListener(&g_PlayerSpawnEvent, "player_spawn", true); + gameeventmanager->AddListener(&g_RoundPreStartEvent, "round_prestart", true); + + bDone = true; + } +} + +void Skin::GameFrame(bool simulating, bool bFirstTick, bool bLastTick) +{ + if (!g_pGameRules) + { + CCSGameRulesProxy* pGameRulesProxy = static_cast(UTIL_FindEntityByClassname(nullptr, "cs_gamerules")); + if (pGameRulesProxy) + { + g_pGameRules = pGameRulesProxy->m_pGameRules(); + } + } + + while (!m_nextFrame.empty()) + { + m_nextFrame.front()(); + m_nextFrame.pop_front(); + } +} + +void CPlayerSpawnEvent::FireGameEvent(IGameEvent* event) +{ + if (!g_pGameRules || g_pGameRules->m_bWarmupPeriod()) + return; + CBasePlayerController* pPlayerController = static_cast(event->GetPlayerController("userid")); + if (!pPlayerController || pPlayerController->m_steamID() == 0) // Ignore bots + return; +} + +void CRoundPreStartEvent::FireGameEvent(IGameEvent* event) +{ + if (g_pGameRules) + { + g_bPistolRound = g_pGameRules->m_totalRoundsPlayed() == 0 || (g_pGameRules->m_bSwitchingTeamsAtRoundReset() && g_pGameRules->m_nOvertimePlaying() == 0) || g_pGameRules->m_bGameRestart(); + } +} + +void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity) +{ + CBasePlayerWeapon* pBasePlayerWeapon = dynamic_cast(pEntity); + if(!pBasePlayerWeapon)return; + g_Skin.NextFrame([pBasePlayerWeapon = pBasePlayerWeapon]() + { + int64_t steamid = pBasePlayerWeapon->m_OriginalOwnerXuidLow(); + if(!steamid)return; + int64_t weaponId = pBasePlayerWeapon->m_AttributeManager().m_Item().m_iItemDefinitionIndex(); + + auto weapon = g_PlayerSkins.find(steamid); + if(weapon == g_PlayerSkins.end())return; + auto skin_parm = weapon->second.find(weaponId); + if(skin_parm == weapon->second.end())return; + + pBasePlayerWeapon->m_nFallbackPaintKit() = skin_parm->second.m_nFallbackPaintKit; + pBasePlayerWeapon->m_nFallbackSeed() = skin_parm->second.m_nFallbackSeed; + pBasePlayerWeapon->m_flFallbackWear() = skin_parm->second.m_flFallbackWear; + + pBasePlayerWeapon->m_AttributeManager().m_Item().m_iItemIDHigh() = -1; + META_CONPRINTF( "steamId: %lld itemId: %d\n", steamid, weaponId); + }); +} + +CON_COMMAND_F(skin, "修改皮肤", FCVAR_CLIENT_CAN_EXECUTE) +{ + if(context.GetPlayerSlot() == -1)return; + CCSPlayerController* pPlayerController = (CCSPlayerController*)g_pEntitySystem->GetBaseEntity((CEntityIndex)(context.GetPlayerSlot().Get() + 1)); + CCSPlayerPawnBase* pPlayerPawn = pPlayerController->m_hPlayerPawn(); + if (!pPlayerPawn || pPlayerPawn->m_lifeState() != LIFE_ALIVE) + return; + char buf[255] = {0}; + if(args.ArgC() != 4) + { + sprintf(buf, " \x04 %s 你使用skin命令修改皮肤需要三个参数!",pPlayerController->m_iszPlayerName()); + FnUTIL_ClientPrintAll(3, buf,nullptr, nullptr, nullptr, nullptr); + return; + } + + CPlayer_WeaponServices* pWeaponServices = pPlayerPawn->m_pWeaponServices(); + + int64_t steamid = pPlayerController->m_steamID(); + int64_t weaponId = pWeaponServices->m_hActiveWeapon()->m_AttributeManager().m_Item().m_iItemDefinitionIndex(); + + auto weapon_name = g_WeaponsMap.find(weaponId); + if(weapon_name == g_WeaponsMap.end())return; + + g_PlayerSkins[steamid][weaponId].m_nFallbackPaintKit = atoi(args.Arg(1)); + g_PlayerSkins[steamid][weaponId].m_nFallbackSeed = atoi(args.Arg(2)); + g_PlayerSkins[steamid][weaponId].m_flFallbackWear = atof(args.Arg(3)); + CBasePlayerWeapon* pPlayerWeapon = pWeaponServices->m_hActiveWeapon(); + + pWeaponServices->RemoveWeapon(pPlayerWeapon); + FnEntityRemove(g_pGameEntitySystem,pPlayerWeapon,nullptr,-1); + FnGiveNamedItem(pPlayerPawn->m_pItemServices(),weapon_name->second.c_str(),nullptr,nullptr,nullptr,nullptr); + pPlayerWeapon->m_AttributeManager().m_Item().m_iAccountID() = 271098320; + //CCSPlayer_ItemServices* pItemServices = static_cast(pPlayerPawn->m_pItemServices()); + //pItemServices->GiveNamedItem(weapon_name->second.c_str()); + // g_pGameRules->PlayerRespawn(static_cast(pPlayerPawn)); + META_CONPRINTF( "called by %lld\n", steamid); + sprintf(buf, " \x04 %s 已经成功修改皮肤 编号:%d 模板:%d 磨损:%f",pPlayerController->m_iszPlayerName(),g_PlayerSkins[steamid][weaponId].m_nFallbackPaintKit,g_PlayerSkins[steamid][weaponId].m_nFallbackSeed,g_PlayerSkins[steamid][weaponId].m_flFallbackWear); + FnUTIL_ClientPrintAll(3, buf,nullptr, nullptr, nullptr, nullptr); +} + +const char* Skin::GetLicense() +{ + return "GPL"; +} + +const char* Skin::GetVersion() +{ + return "1.0.0"; +} + +const char* Skin::GetDate() +{ + return __DATE__; +} + +const char* Skin::GetLogTag() +{ + return "skin"; +} + +const char* Skin::GetAuthor() +{ + return "宇宙遨游"; +} + +const char* Skin::GetDescription() +{ + return "武器皮肤插件"; +} + +const char* Skin::GetName() +{ + return "武器皮肤插件"; +} + +const char* Skin::GetURL() +{ + return "http://cs2.wssr.top"; +} diff --git a/Skin.h b/Skin.h new file mode 100644 index 0000000..1cc1c09 --- /dev/null +++ b/Skin.h @@ -0,0 +1,67 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ====================================================== + * Mini VIP + * Written by Phoenix (˙·٠●Феникс●٠·˙) 2023. + * ====================================================== + * + * 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 software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + */ + +#ifndef _INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_ +#define _INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_ + +#include +#include +#include +#include +#include "igameevents.h" +#include "vector.h" +#include +#include + +class Skin final : public ISmmPlugin, public IMetamodListener +{ +public: + bool Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late); + bool Unload(char* error, size_t maxlen); + void NextFrame(std::function fn); +private: + const char* GetAuthor(); + const char* GetName(); + const char* GetDescription(); + const char* GetURL(); + const char* GetLicense(); + const char* GetVersion(); + const char* GetDate(); + const char* GetLogTag(); + +private: // Hooks + void StartupServer(const GameSessionConfiguration_t& config, ISource2WorldSession*, const char*); + void GameFrame(bool simulating, bool bFirstTick, bool bLastTick); + + std::deque> m_nextFrame; +}; + +class CPlayerSpawnEvent : public IGameEventListener2 +{ + void FireGameEvent(IGameEvent* event) override; +}; + +class CRoundPreStartEvent : public IGameEventListener2 +{ + void FireGameEvent(IGameEvent* event) override; +}; + +class CEntityListener : public IEntityListener +{ + void OnEntitySpawned(CEntityInstance* pEntity) override; +}; + +#endif //_INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_ diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..c129d46 --- /dev/null +++ b/build.bat @@ -0,0 +1,3 @@ +mkdir build && cd build +python ../configure.py --enable-optimize --plugin-name=Skin --plugin-alias=Skin --sdks=cs2 --targets=x86_64 --mms_path=D:/alliedmodders/metamod-source --hl2sdk-root=D:/alliedmodders/hl2sdk-root/ +ambuild \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..63f98ca --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +mkdir build && cd build +python3 ../configure.py --enable-optimize --plugin-name=Skin --plugin-alias=Skin --sdks=cs2 --mms_path=/root/metamod-source --hl2sdk-root=/root/hl2sdk-root/ +ambuild diff --git a/configure.py b/configure.py new file mode 100644 index 0000000..2ca9277 --- /dev/null +++ b/configure.py @@ -0,0 +1,39 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et: +import sys +try: + from ambuild2 import run, util +except: + try: + import ambuild + sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n') + sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n') + except: + sys.stderr.write('AMBuild must be installed to build this project.\n') + sys.stderr.write('http://www.alliedmods.net/ambuild\n') + sys.exit(1) + +# Hack to show a decent upgrade message, which wasn't done until 2.2. +ambuild_version = getattr(run, 'CURRENT_API', '2.1') +if ambuild_version.startswith('2.1'): + sys.stderr.write("AMBuild 2.2 or higher is required; please update\n") + sys.exit(1) + +parser = run.BuildParser(sourcePath=sys.path[0], api='2.2') +parser.options.add_argument('-n', '--plugin-name', type=str, dest='plugin_name', default=None, + help='Plugin name') +parser.options.add_argument('-a', '--plugin-alias', type=str, dest='plugin_alias', default=None, + help='Plugin alias') +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='Metamod:Source source tree folder') +parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', + help='Enable debugging symbols') +parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt', + help='Enable optimization') +parser.options.add_argument('-s', '--sdks', default='all', dest='sdks', + help='Build against specified SDKs; valid args are "all", "present", or ' + 'comma-delimited list of engine names (default: "all")') +parser.options.add_argument('--targets', type=str, dest='targets', default=None, + help="Override the target architecture (use commas to separate multiple targets).") +parser.Configure() diff --git a/sdk/CBaseAnimGraph.h b/sdk/CBaseAnimGraph.h new file mode 100644 index 0000000..b3e2e1d --- /dev/null +++ b/sdk/CBaseAnimGraph.h @@ -0,0 +1,8 @@ +#pragma once +#include "CBaseModelEntity.h" +#include "schemasystem.h" + +class CBaseAnimGraph : public CBaseModelEntity +{ +public: +}; \ No newline at end of file diff --git a/sdk/CBaseCSGrenadeProjectile.h b/sdk/CBaseCSGrenadeProjectile.h new file mode 100644 index 0000000..734bdaf --- /dev/null +++ b/sdk/CBaseCSGrenadeProjectile.h @@ -0,0 +1,7 @@ +#pragma once +#include "CBaseGrenade.h" + +class CBaseCSGrenadeProjectile : public CBaseGrenade +{ +public: +}; \ No newline at end of file diff --git a/sdk/CBaseCombatCharacter.h b/sdk/CBaseCombatCharacter.h new file mode 100644 index 0000000..3790245 --- /dev/null +++ b/sdk/CBaseCombatCharacter.h @@ -0,0 +1,8 @@ +#pragma once +#include "CBaseFlex.h" +#include "schemasystem.h" + +class CBaseCombatCharacter : public CBaseFlex +{ +public: +}; \ No newline at end of file diff --git a/sdk/CBaseEntity.h b/sdk/CBaseEntity.h new file mode 100644 index 0000000..9077290 --- /dev/null +++ b/sdk/CBaseEntity.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include "schemasystem.h" +#include "ehandle.h" + +inline CEntityInstance* UTIL_FindEntityByClassname(CEntityInstance* pStart, const char* name) +{ + extern CEntitySystem* g_pEntitySystem; + CEntityIdentity* pEntity = pStart ? pStart->m_pEntity->m_pNext : g_pEntitySystem->m_EntityList.m_pFirstActiveEntity; + + for (; pEntity; pEntity = pEntity->m_pNext) + { + if (!strcmp(pEntity->m_designerName.String(), name)) + return pEntity->m_pInstance; + }; + + return nullptr; +} + +class SC_CBaseEntity : public CBaseEntity +{ +public: + SCHEMA_FIELD(int32_t, CBaseEntity, m_iHealth); + SCHEMA_FIELD(int32_t, CBaseEntity, m_iMaxHealth); + SCHEMA_FIELD(LifeState_t, CBaseEntity, m_lifeState); + SCHEMA_FIELD(uint8_t, CBaseEntity, m_iTeamNum); + SCHEMA_FIELD(float, CBaseEntity, m_flGravityScale); +}; diff --git a/sdk/CBaseFlex.h b/sdk/CBaseFlex.h new file mode 100644 index 0000000..98f8c75 --- /dev/null +++ b/sdk/CBaseFlex.h @@ -0,0 +1,8 @@ +#pragma once +#include "CBaseAnimGraph.h" +#include "schemasystem.h" + +class CBaseFlex : public CBaseAnimGraph +{ +public: +}; \ No newline at end of file diff --git a/sdk/CBaseGrenade.h b/sdk/CBaseGrenade.h new file mode 100644 index 0000000..7d81b97 --- /dev/null +++ b/sdk/CBaseGrenade.h @@ -0,0 +1,16 @@ +#pragma once +#include "CBaseFlex.h" +#include "CCSPlayerPawn.h" +#include "schemasystem.h" + +class CBaseGrenade : public CBaseFlex +{ +public: + SCHEMA_FIELD(CHandle, CBaseGrenade, m_hThrower); +}; + +class CCSWeaponBase : public CBasePlayerWeapon +{ +public: + SCHEMA_FIELD(CHandle, CCSWeaponBase, m_hPrevOwner); +}; \ No newline at end of file diff --git a/sdk/CBaseModelEntity.h b/sdk/CBaseModelEntity.h new file mode 100644 index 0000000..a201b79 --- /dev/null +++ b/sdk/CBaseModelEntity.h @@ -0,0 +1,8 @@ +#pragma once +#include "CBaseEntity.h" +#include "schemasystem.h" + +class CBaseModelEntity : public SC_CBaseEntity +{ +public: +}; \ No newline at end of file diff --git a/sdk/CBasePlayerController.h b/sdk/CBasePlayerController.h new file mode 100644 index 0000000..08dd707 --- /dev/null +++ b/sdk/CBasePlayerController.h @@ -0,0 +1,13 @@ +#pragma once +#include "CBaseEntity.h" +#include "CBasePlayerPawn.h" +#include "ehandle.h" +#include "schemasystem.h" + +class CBasePlayerController : public SC_CBaseEntity +{ +public: + SCHEMA_FIELD(CHandle, CBasePlayerController, m_hPawn); + SCHEMA_FIELD(char[128], CBasePlayerController, m_iszPlayerName); + SCHEMA_FIELD(uint64_t, CBasePlayerController, m_steamID); +}; \ No newline at end of file diff --git a/sdk/CBasePlayerPawn.h b/sdk/CBasePlayerPawn.h new file mode 100644 index 0000000..48615d6 --- /dev/null +++ b/sdk/CBasePlayerPawn.h @@ -0,0 +1,14 @@ +#pragma once +#include "CBaseCombatCharacter.h" +#include "CPlayer_ItemServices.h" +#include "CBasePlayerController.h" +#include "ehandle.h" +#include "schemasystem.h" + +class CBasePlayerPawn : public CBaseCombatCharacter +{ +public: + SCHEMA_FIELD(CPlayer_WeaponServices*, CBasePlayerPawn, m_pWeaponServices); + SCHEMA_FIELD(CPlayer_ItemServices*, CBasePlayerPawn, m_pItemServices); + SCHEMA_FIELD(CHandle, CBasePlayerPawn, m_hController); +}; \ No newline at end of file diff --git a/sdk/CCSPlayerController.h b/sdk/CCSPlayerController.h new file mode 100644 index 0000000..7936307 --- /dev/null +++ b/sdk/CCSPlayerController.h @@ -0,0 +1,15 @@ +#pragma once +#include "CCSPlayerController_InGameMoneyServices.h" +#include "CBasePlayerController.h" +#include "CCSPlayerPawn.h" +#include "ehandle.h" +#include "schemasystem.h" + +class CCSPlayerController : public CBasePlayerController +{ +public: + SCHEMA_FIELD(CCSPlayerController_InGameMoneyServices*, CCSPlayerController, m_pInGameMoneyServices); + SCHEMA_FIELD(CUtlSymbolLarge, CCSPlayerController, m_szClan); + SCHEMA_FIELD(char[32], CCSPlayerController, m_szClanName); + SCHEMA_FIELD(CHandle, CCSPlayerController, m_hPlayerPawn); +}; \ No newline at end of file diff --git a/sdk/CCSPlayerController_InGameMoneyServices.h b/sdk/CCSPlayerController_InGameMoneyServices.h new file mode 100644 index 0000000..b3bcf81 --- /dev/null +++ b/sdk/CCSPlayerController_InGameMoneyServices.h @@ -0,0 +1,9 @@ +#pragma once +#include "schemasystem.h" + +class CCSPlayerController_InGameMoneyServices +{ +public: + SCHEMA_FIELD(int32_t, CCSPlayerController_InGameMoneyServices, m_iAccount); + SCHEMA_FIELD(int32_t, CCSPlayerController_InGameMoneyServices, m_iStartAccount); +}; \ No newline at end of file diff --git a/sdk/CCSPlayerPawn.h b/sdk/CCSPlayerPawn.h new file mode 100644 index 0000000..6469b49 --- /dev/null +++ b/sdk/CCSPlayerPawn.h @@ -0,0 +1,8 @@ +#pragma once +#include "CCSPlayerPawnBase.h" +#include "schemasystem.h" + +class CCSPlayerPawn : public CCSPlayerPawnBase +{ +public: +}; \ No newline at end of file diff --git a/sdk/CCSPlayerPawnBase.h b/sdk/CCSPlayerPawnBase.h new file mode 100644 index 0000000..7d4c9b5 --- /dev/null +++ b/sdk/CCSPlayerPawnBase.h @@ -0,0 +1,9 @@ +#pragma once +#include "CBasePlayerPawn.h" +#include "schemasystem.h" + +class CCSPlayerPawnBase : public CBasePlayerPawn +{ +public: + SCHEMA_FIELD(int32_t, CCSPlayerPawnBase, m_ArmorValue); +}; \ No newline at end of file diff --git a/sdk/CCSPlayer_ItemServices.h b/sdk/CCSPlayer_ItemServices.h new file mode 100644 index 0000000..4a4c5be --- /dev/null +++ b/sdk/CCSPlayer_ItemServices.h @@ -0,0 +1,33 @@ +#pragma once +#include "CPlayer_ItemServices.h" +#include "CBaseEntity.h" +#include "schemasystem.h" + +class CCSPlayer_ItemServices : public CPlayer_ItemServices +{ +public: + virtual ~CCSPlayer_ItemServices() = 0; +private: + virtual void unk_01() = 0; + virtual void unk_02() = 0; + virtual void unk_03() = 0; + virtual void unk_04() = 0; + virtual void unk_05() = 0; + virtual void unk_06() = 0; + virtual void unk_07() = 0; + virtual void unk_08() = 0; + virtual void unk_09() = 0; + virtual void unk_10() = 0; + virtual void unk_11() = 0; + virtual void unk_12() = 0; + virtual void unk_13() = 0; + virtual void unk_14() = 0; + virtual SC_CBaseEntity* _GiveNamedItem(const char* pchName) = 0; +public: + virtual bool GiveNamedItemBool(const char* pchName) = 0; + virtual SC_CBaseEntity* GiveNamedItem(const char* pchName) = 0; + + SCHEMA_FIELD(bool, CCSPlayer_ItemServices, m_bHasDefuser); + SCHEMA_FIELD(bool, CCSPlayer_ItemServices, m_bHasHelmet); + SCHEMA_FIELD(bool, CCSPlayer_ItemServices, m_bHasHeavyArmor); +}; \ No newline at end of file diff --git a/sdk/CGameRules.h b/sdk/CGameRules.h new file mode 100644 index 0000000..0ea3977 --- /dev/null +++ b/sdk/CGameRules.h @@ -0,0 +1,43 @@ +#pragma once +#include "schemasystem.h" +#include "CCSPlayerPawn.h" + +enum GamePhase : int32_t +{ + GAMEPHASE_WARMUP_ROUND, + GAMEPHASE_PLAYING_STANDARD, + GAMEPHASE_PLAYING_FIRST_HALF, + GAMEPHASE_PLAYING_SECOND_HALF, + GAMEPHASE_HALFTIME, + GAMEPHASE_MATCH_ENDED, + GAMEPHASE_MAX +}; + +class CGameRules +{ +public: +}; + +class CMultiplayRules : public CGameRules +{ +public: +}; + +class CTeamplayRules : public CMultiplayRules +{ +public: +}; + +class CCSGameRules : public CTeamplayRules +{ +public: + SCHEMA_FIELD(bool, CCSGameRules, m_bWarmupPeriod); + SCHEMA_FIELD(bool, CCSGameRules, m_bGameRestart); + SCHEMA_FIELD(GamePhase, CCSGameRules, m_gamePhase); + SCHEMA_FIELD(int32_t, CCSGameRules, m_totalRoundsPlayed); + SCHEMA_FIELD(int32_t, CCSGameRules, m_nOvertimePlaying); + SCHEMA_FIELD(bool, CCSGameRules, m_bSwitchingTeamsAtRoundReset); + auto PlayerRespawn(CCSPlayerPawn* PlayerPawn) { + return CALL_VIRTUAL(void, 110, this, PlayerPawn); + } +}; \ No newline at end of file diff --git a/sdk/CGameRulesProxy.h b/sdk/CGameRulesProxy.h new file mode 100644 index 0000000..ba7f482 --- /dev/null +++ b/sdk/CGameRulesProxy.h @@ -0,0 +1,15 @@ +#pragma once +#include "CBaseEntity.h" +#include "schemasystem.h" +#include "CGameRules.h" + +class CGameRulesProxy : public SC_CBaseEntity +{ +public: +}; + +class CCSGameRulesProxy : public CGameRulesProxy +{ +public: + SCHEMA_FIELD(CCSGameRules*, CCSGameRulesProxy, m_pGameRules); +}; \ No newline at end of file diff --git a/sdk/CPlayerPawnComponent.h b/sdk/CPlayerPawnComponent.h new file mode 100644 index 0000000..76eedea --- /dev/null +++ b/sdk/CPlayerPawnComponent.h @@ -0,0 +1,8 @@ +#pragma once +#include "schemasystem.h" + +class CPlayerPawnComponent +{ +public: + virtual ~CPlayerPawnComponent() = 0; +}; \ No newline at end of file diff --git a/sdk/CPlayer_ItemServices.h b/sdk/CPlayer_ItemServices.h new file mode 100644 index 0000000..20479fb --- /dev/null +++ b/sdk/CPlayer_ItemServices.h @@ -0,0 +1,66 @@ +#pragma once +#include "CPlayerPawnComponent.h" +#include "schemasystem.h" +#include "ehandle.h" +#include "CBaseFlex.h" + +class CEconEntity : public CBaseFlex +{ +public: +}; + +class CEconItemAttribute +{ +public: + SCHEMA_FIELD(uint16_t, CEconItemAttribute, m_iAttributeDefinitionIndex); + SCHEMA_FIELD(float, CEconItemAttribute, m_flValue); + SCHEMA_FIELD(float, CEconItemAttribute, m_flInitialValue); +}; + +class CAttributeList +{ +public: + SCHEMA_FIELD(int64_t, CAttributeList, m_Attributes); +}; + +class CEconItemView +{ +public: + SCHEMA_FIELD(CAttributeList, CEconItemView, m_AttributeList); + SCHEMA_FIELD(int32_t, CEconItemView, m_iItemIDHigh); + SCHEMA_FIELD(int32_t, CEconItemView, m_iAccountID); + SCHEMA_FIELD(uint16_t, CEconItemView, m_iItemDefinitionIndex); +}; + +class CAttributeContainer +{ +public: + SCHEMA_FIELD(CEconItemView, CAttributeContainer, m_Item); +}; + +class CBasePlayerWeapon : public CEconEntity +{ +public: + SCHEMA_FIELD(CAttributeContainer, CEconEntity, m_AttributeManager); + SCHEMA_FIELD(int32_t, CEconEntity, m_nFallbackPaintKit); + SCHEMA_FIELD(int32_t, CEconEntity, m_nFallbackSeed); + SCHEMA_FIELD(int32_t, CEconEntity, m_nFallbackStatTrak); + SCHEMA_FIELD(float, CEconEntity, m_flFallbackWear); + SCHEMA_FIELD(uint64_t, CEconEntity, m_OriginalOwnerXuidLow); +}; + +class CPlayer_WeaponServices : public CPlayerPawnComponent +{ +public: + virtual ~CPlayer_WeaponServices() = 0; + SCHEMA_FIELD(CHandle, CPlayer_WeaponServices, m_hActiveWeapon); + auto RemoveWeapon(CBasePlayerWeapon* weapon) { + return CALL_VIRTUAL(void, 20, this, weapon, nullptr, nullptr); + } +}; + +class CPlayer_ItemServices : public CPlayerPawnComponent +{ +public: + virtual ~CPlayer_ItemServices() = 0; +}; \ No newline at end of file diff --git a/sdk/CSmokeGrenadeProjectile.h b/sdk/CSmokeGrenadeProjectile.h new file mode 100644 index 0000000..a81c366 --- /dev/null +++ b/sdk/CSmokeGrenadeProjectile.h @@ -0,0 +1,10 @@ +#pragma once +#include "CBaseCSGrenadeProjectile.h" +#include "schemasystem.h" +#include "vector.h" + +class CSmokeGrenadeProjectile : public CBaseCSGrenadeProjectile +{ +public: + SCHEMA_FIELD(Vector, CSmokeGrenadeProjectile, m_vSmokeColor); +}; \ No newline at end of file diff --git a/sdk/schemasystem.cpp b/sdk/schemasystem.cpp new file mode 100644 index 0000000..49caebe --- /dev/null +++ b/sdk/schemasystem.cpp @@ -0,0 +1,44 @@ +#include "schemasystem.h" +#include "utils.hpp" +#include + +void CSchemaSystemTypeScope::FindDeclaredClass(SchemaClassInfoData_t*& pClassInfo, const char* pszClassName) +{ +#if defined _WIN32 && _M_X64 + CallVFunc(this, pClassInfo, pszClassName); +#else + pClassInfo = CallVFunc(this, pszClassName); +#endif +} + +CSchemaSystemTypeScope* CSchemaSystem::FindTypeScopeForModule(const char* szpModuleName) +{ + return CallVFunc(this, szpModuleName, nullptr); +} + +CSchemaSystemTypeScope* CSchemaSystem::GetServerTypeScope() +{ + static CSchemaSystemTypeScope* pServerTypeScope = FindTypeScopeForModule(WIN_LINUX("server.dll", "libserver.so")); + + return pServerTypeScope; +} + +int32_t CSchemaSystem::GetServerOffset(const char* pszClassName, const char* pszPropName) +{ + SchemaClassInfoData_t* pClassInfo = nullptr; + GetServerTypeScope()->FindDeclaredClass(pClassInfo, pszClassName); + if (pClassInfo) + { + for (int i = 0; i < pClassInfo->m_iFieldsCount; i++) + { + auto& pFieldData = pClassInfo->m_pFieldsData[i]; + + if (std::strcmp(pFieldData.m_pszName, pszPropName) == 0) + { + return pFieldData.m_iOffset; + } + } + } + + return -1; +} \ No newline at end of file diff --git a/sdk/schemasystem.h b/sdk/schemasystem.h new file mode 100644 index 0000000..1565190 --- /dev/null +++ b/sdk/schemasystem.h @@ -0,0 +1,93 @@ +//====== Copyright �, Valve Corporation, All rights reserved. ======= +// +// Purpose: Additional shared object cache functionality for the GC +// +//============================================================================= + +#ifndef SCHEMASYSTEM_H +#define SCHEMASYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +#if defined(_WIN32) +#define FASTCALL __fastcall +#define THISCALL __thiscall +#else +#define FASTCALL __attribute__((fastcall)) +#define THISCALL +#define strtok_s strtok_r +#endif + +#define CALL_VIRTUAL(retType, idx, ...) \ + vmt::CallVirtual(idx, __VA_ARGS__) +namespace vmt { + template + inline T GetVMethod(uint32_t uIndex, void* pClass) { + void** pVTable = *static_cast(pClass); + return reinterpret_cast(pVTable[uIndex]); + } + + template + inline T CallVirtual(uint32_t uIndex, void* pClass, Args... args) { + auto pFunc = GetVMethod(uIndex, pClass); + return pFunc(pClass, args...); + } +} // namespace vmt + +struct SchemaClassFieldData_t +{ + const char* m_pszName; // 0x0000 + void* m_pSchemaType; // 0x0008 + int32_t m_iOffset; // 0x0010 + int32_t m_iMetaDataSize; // 0x0014 + void* m_pMetaData; // 0x0018 +}; + +struct SchemaClassInfoData_t +{ + char pad_0x0000[0x8]; // 0x0000 + + const char* m_pszName; // 0x0008 + const char* m_pszModule; // 0x0010 + + int m_iSize; // 0x0018 + int16_t m_iFieldsCount; // 0x001C + + int16_t m_iStaticSize; // 0x001E + int16_t m_iMetadataSize; // 0x0020 + int16_t m_iUnk1; // 0x0022 + int16_t m_iUnk2; // 0x0024 + int16_t m_iUnk3; // 0x0026 + + SchemaClassFieldData_t* m_pFieldsData; // 0x0028 +}; + +class CSchemaSystemTypeScope +{ +public: + void FindDeclaredClass(SchemaClassInfoData_t*& pClassInfo, const char* pszClassName); +}; + +class CSchemaSystem +{ +public: + CSchemaSystemTypeScope* FindTypeScopeForModule(const char* szpModuleName); + CSchemaSystemTypeScope* GetServerTypeScope(); + int32_t GetServerOffset(const char* pszClassName, const char* pszPropName); +}; + +extern CSchemaSystem* g_pCSchemaSystem; + +#define SCHEMA_FIELD(type, className, propName) \ + std::add_lvalue_reference_t propName() \ + { \ + static const int32_t offset = g_pCSchemaSystem->GetServerOffset(#className, #propName); \ + return *reinterpret_cast>(reinterpret_cast(this) + offset); \ + } + + +#endif //SCHEMASYSTEM_H diff --git a/steamid.py b/steamid.py new file mode 100644 index 0000000..dd5ec5e --- /dev/null +++ b/steamid.py @@ -0,0 +1,224 @@ +import re +import math + +# Define some regex stuff +STEAM_ID_REGEX = "^STEAM_" +STEAM_ID_3_REGEX = "^\[.*\]$" + +# steamID64 are all offset from this value +ID64_BASE = 76561197960265728 + +def convert_steamID(steamID, target_format:str, as_int=False): + """ + Wrapper for conversion methods to allow you to call different conversions via the same function + + Parameters + ---------- + steamID : int or str + steamID of any format to convert + + target_format : str + Format to convert steamId to + Possible values are: SteamID, SteamID3, SteamID64 + + as_int : bool + If a SteamId64 is returned as an int or a string + Only used when target_format = SteamId64 + Default = False + + + Returns + ------- + int or str + steamID value + + """ + + if target_format == 'SteamID': + return to_steamID(steamID) + + elif target_format == 'SteamID3': + return to_steamID3(steamID) + + elif target_format == 'SteamID64': + return to_steamID64(steamID, as_int) + + else: + raise ValueError("Incorrect target Steam ID format. Target_format must be one of: SteamID, SteamID3, SteamID64") + +def to_steamID(steamID): + """ + Convert to steamID + + A steamID is unique to each steam account, + Formatted with digits as x "STEAM_0:x:xxxxxxxx" + + Parameters + ---------- + steamID : int or str + steamID3 or steamID64 to convert to steamID + + Returns + ------- + str + steamID value + + """ + + id_str = str(steamID) + + if re.search(STEAM_ID_REGEX, id_str): # Already a steamID + return id_str + + elif re.search(STEAM_ID_3_REGEX, id_str): # If passed steamID3 + + id_split = id_str.split(":") # Split string into 'Universe', Account type, and Account number + account_id3 = int(id_split[2][:-1]) # Remove ] from end of steamID3 + + account_type = account_id3 % 2 + + account_id = (account_id3 - account_type) // 2 + + elif id_str.isnumeric(): # Passed steamID64 + + check_steamID64_length(id_str) # Validate id passed in + + offset_id = int(id_str) - ID64_BASE + + # Get the account type and id + account_type = offset_id % 2 + + account_id = ((offset_id - account_type) // 2) + + return "STEAM_0:" + str(account_type) + ":" + str(account_id) + +def to_steamID3(steamID): + """ + Convert to steamID3 + + A steamID3 is unique to each steam account, + Formatted with digits as x "[U:1:xxxxxxxx]" + + Parameters + ---------- + steamID : int or str + steamID or steamID64 to convert to steamID3 + + Returns + ------- + str + steamID3 value + + """ + + id_str = str(steamID) + + if re.search(STEAM_ID_3_REGEX, id_str): # Already a steamID3 + return id_str + + elif re.search(STEAM_ID_REGEX, id_str): # If passed steamID + + id_split = id_str.split(":") # Split string into 'Universe', Account type, and Account number + + account_type = int(id_split[1]) # Check for account type + account_id = int(id_split[2]) # Account number, needs to be doubled when added to id3 + + # Join together in steamID3 format + return "[U:1:" + str(((account_id + account_type) * 2) - account_type) + "]" + + elif id_str.isnumeric(): # Passed steamID64 + + check_steamID64_length(id_str) # Validate id passed in + + offset_id = int(id_str) - ID64_BASE + + # Get the account type and id + account_type = offset_id % 2 + + account_id = ((offset_id - account_type) // 2) + account_type + + # Join together in steamID3 format + return "[U:1:" + str((account_id * 2) - account_type) + "]" + + else: + raise ValueError(f"Unable to decode steamID: {steamID}") + + +def to_steamID64(steamID, as_int = False): + """ + Convert to steamID64 + + A steamID64 is a 17 digit number, unique to each steam account + + Parameters + ---------- + steamID : int or str + steamID or steamID3 to convert to steamID64 + as_int : bool + If the steamID64 is returned as an integer rather than string, Default = False + + Returns + ------- + int or str + steamID64 value + + """ + + id_str = str(steamID) + id_split = id_str.split(":") # Split string into 'Universe', Account type, and Account number + + if id_str.isnumeric(): # Already a steamID64 + + check_steamID64_length(id_str) # Validate id passed in + if as_int: + return int(id_str) + else: + return id_str + + elif re.search(STEAM_ID_REGEX, id_str): # If passed steamID + + account_type = int(id_split[1]) # Check for account type + account_id = int(id_split[2]) # Account number, needs to be doubled when added to id64 + + elif re.search(STEAM_ID_3_REGEX, id_str): # If passed steamID3 + + account_id3 = int(id_split[2][:-1]) # Remove ] from end of steamID3 + + account_type = account_id3 % 2 + + account_id = (account_id3 - account_type) // 2 + print(account_id3) + print(account_type) + print(account_id) + + else: + raise ValueError(f"Unable to decode steamID: {steamID}") + + + id64 = ID64_BASE + (account_id * 2) + account_type + + # Check if returning as string or integer + if as_int: + return id64 + else: + return str(id64) + + +def check_steamID64_length(id_str :str): + """ + Check if a steamID64 is of the correct length, raises ValueError if not. + + Not really for you to use + + Parameters + ---------- + id_str : str + steamID64 to check length of + + """ + + if len(id_str) != 17: + raise ValueError(f"Incorrect length for steamID64: {id_str}") + + +print(to_steamID64('[U:1:271098320]')) diff --git a/utils.hpp b/utils.hpp new file mode 100644 index 0000000..6933672 --- /dev/null +++ b/utils.hpp @@ -0,0 +1,14 @@ +#pragma once + +template +constexpr T CallVFunc(void* pThis, Args... args) noexcept +{ + return reinterpret_cast (reinterpret_cast(pThis)[0][index])(pThis, args...); +} + +#ifdef _WIN32 +#define WIN_LINUX(win, linux) win +#else +#define WIN_LINUX(win, linux) linux +#endif + diff --git a/utils/memaddr.cpp b/utils/memaddr.cpp new file mode 100644 index 0000000..13e7dd6 --- /dev/null +++ b/utils/memaddr.cpp @@ -0,0 +1,66 @@ +#include "memaddr.h" + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddress wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCall(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) +{ + return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddressSelf wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCallSelf(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) +{ + return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddress(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + return CMemory(nextInstruction + relativeAddress); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset from current address +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddressSelf(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + ptr = nextInstruction + relativeAddress; + return *this; +} diff --git a/utils/memaddr.h b/utils/memaddr.h new file mode 100644 index 0000000..058a662 --- /dev/null +++ b/utils/memaddr.h @@ -0,0 +1,117 @@ +#ifndef MEMADDR_H +#define MEMADDR_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +class CMemory +{ +public: + enum class Direction : int + { + DOWN = 0, + UP, + }; + + CMemory(void) = default; + CMemory(const uintptr_t ptr) : ptr(ptr) {} + CMemory(const void* ptr) : ptr(uintptr_t(ptr)) {} + + inline operator uintptr_t(void) const + { + return ptr; + } + + inline operator void*(void) const + { + return reinterpret_cast(ptr); + } + + inline operator bool(void) const + { + return ptr != 0; + } + + inline bool operator!= (const CMemory& addr) const + { + return ptr != addr.ptr; + } + + inline bool operator== (const CMemory& addr) const + { + return ptr == addr.ptr; + } + + inline bool operator== (const uintptr_t& addr) const + { + return ptr == addr; + } + + inline uintptr_t GetPtr(void) const + { + return ptr; + } + + template inline T GetValue(void) const + { + return *reinterpret_cast(ptr); + } + + template inline T CCast(void) const + { + return (T)ptr; + } + + template inline T RCast(void) const + { + return reinterpret_cast(ptr); + } + + inline CMemory Offset(ptrdiff_t offset) const + { + return CMemory(ptr + offset); + } + + inline CMemory OffsetSelf(ptrdiff_t offset) + { + ptr += offset; + return *this; + } + + inline CMemory Deref(int deref = 1) const + { + uintptr_t reference = ptr; + + while (deref--) + { + if (reference) + reference = *reinterpret_cast(reference); + } + + return CMemory(reference); + } + + inline CMemory DerefSelf(int deref = 1) + { + while (deref--) + { + if (ptr) + ptr = *reinterpret_cast(ptr); + } + + return *this; + } + + CMemory FollowNearCall(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5); + CMemory FollowNearCallSelf(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5); + CMemory ResolveRelativeAddress(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4); + CMemory ResolveRelativeAddressSelf(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4); + +private: + uintptr_t ptr = 0; +}; + +#endif // MEMADDR_H diff --git a/utils/module.cpp b/utils/module.cpp new file mode 100644 index 0000000..712949e --- /dev/null +++ b/utils/module.cpp @@ -0,0 +1,279 @@ +#include +#include +#include "module.h" +#if defined _WIN32 && _M_X64 +#include +#elif defined __linux__ && __x86_64__ +#include +#include +#include +#else +#error "Unsupported platform" +#endif + +/////////////////////////////////////////////////////////////////////////////// +// For converting a string pattern with wildcards to an array of bytes and mask. +std::pair, std::string> PatternToMaskedBytes(const std::string_view svInput) +{ + char* pszPatternStart = const_cast(svInput.data()); + char* pszPatternEnd = pszPatternStart + svInput.size(); + std::vector vBytes; + std::string svMask; + + for (char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + + vBytes.push_back(0); // Push the byte back as invalid. + svMask += '?'; + } + else + { + vBytes.push_back(static_cast(strtoul(pszCurrentByte, &pszCurrentByte, 16))); + svMask += 'x'; + } + } + + return make_pair(vBytes, svMask); +}; + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : svModuleName +//----------------------------------------------------------------------------- +CModule::CModule(const std::string_view svModuleName) : m_svModuleName(svModuleName) +{ +#if defined _WIN32 && _M_X64 + m_pModuleBase = reinterpret_cast(GetModuleHandleA(svModuleName.data())); +#else + struct dl_data + { + ElfW(Addr) addr; + const char* moduleName; + } dldata{0, svModuleName.data()}; + + dl_iterate_phdr([](dl_phdr_info* info, size_t /* size */, void* data) + { + dl_data* dldata = reinterpret_cast(data); + + if (strstr(info->dlpi_name, dldata->moduleName) != nullptr) + { + dldata->addr = info->dlpi_addr; + } + + return 0; + }, &dldata); + + m_pModuleBase = reinterpret_cast(dldata.addr); +#endif + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : addr +//----------------------------------------------------------------------------- +CModule::CModule(const CMemory addr) +{ +#if defined _WIN32 && _M_X64 + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr, &mbi, sizeof(mbi)) || mbi.AllocationBase == nullptr) + { + return; + } + + m_pModuleBase = reinterpret_cast(mbi.AllocationBase); + + char szPath[MAX_PATH]; + size_t nLen = GetModuleFileNameA(reinterpret_cast(mbi.AllocationBase), szPath, sizeof(szPath)); + m_svModuleName.assign(szPath, nLen); +#else + Dl_info info; + if (!dladdr(addr, &info) || !info.dli_fbase || !info.dli_fname) + { + return; + } + + m_pModuleBase = reinterpret_cast(info.dli_fbase); + m_svModuleName.assign(info.dli_fname); +#endif + + m_svModuleName.assign(m_svModuleName.substr(m_svModuleName.find_last_of("/\\") + 1)); // Leave only file name. + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: initializes module descriptors +//----------------------------------------------------------------------------- +void CModule::Init() +{ +#if defined _WIN32 && _M_X64 + IMAGE_DOS_HEADER* pDOSHeader = reinterpret_cast(m_pModuleBase); + IMAGE_NT_HEADERS64* pNTHeaders = reinterpret_cast(m_pModuleBase + pDOSHeader->e_lfanew); + + const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(pNTHeaders); // Get first image section. + + for (WORD i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. + { + const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. + m_vModuleSections.push_back(ModuleSections_t(reinterpret_cast(hCurrentSection.Name), static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. + } +#else + Elf64_Ehdr* pEhdr = reinterpret_cast(m_pModuleBase); + Elf64_Phdr* pPhdr = reinterpret_cast(m_pModuleBase + pEhdr->e_phoff); + + for (Elf64_Half i = 0; i < pEhdr->e_phnum; i++) // Loop through the sections. + { + Elf64_Phdr& phdr = pPhdr[i]; + + if (phdr.p_type == PT_LOAD && phdr.p_flags == (PF_X | PF_R)) + { + /* From glibc, elf/dl-load.c: + * c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) + * & ~(GLRO(dl_pagesize) - 1)); + * + * In glibc, the segment file size is aligned up to the nearest page size and + * added to the virtual address of the segment. We just want the size here. + */ + size_t pagesize = sysconf(_SC_PAGESIZE); + size_t nSectionSize = (phdr.p_filesz + pagesize - 1) & ~(pagesize - 1); + + // We can't get get the section names, but most likely it will be exactly .text. + m_vModuleSections.push_back(ModuleSections_t(".text", m_pModuleBase + phdr.p_vaddr, nSectionSize)); + + break; + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: initializes the default executable segments +//----------------------------------------------------------------------------- +void CModule::LoadSections() +{ + m_ExecutableCode = GetSectionByName(".text"); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory using SIMD instructions +// Input : *szPattern - +// *szMask - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const uint8_t* szPattern, const char* szMask, const ModuleSections_t* moduleSection) const +{ + const ModuleSections_t* section = moduleSection ? moduleSection : &m_ExecutableCode; + if (!section->IsSectionValid()) + { + return CMemory(); + } + + const uintptr_t nBase = section->m_pSectionBase; + const size_t nSize = section->m_nSectionSize; + + const size_t nMaskLen = strlen(szMask); + const uint8_t* pData = reinterpret_cast(nBase); + const uint8_t* pEnd = pData + nSize - nMaskLen; + + int nMasks[64]; // 64*16 = enough masks for 1024 bytes. + const uint8_t iNumMasks = static_cast(std::ceil(static_cast(nMaskLen) / 16.f)); + + memset(nMasks, 0, iNumMasks * sizeof(int)); + for (uint8_t i = 0; i < iNumMasks; i++) + { + for (int8_t j = std::min(nMaskLen - static_cast(i) * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + nMasks[i] |= 1 << j; + } + } + } + + const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(szPattern)); + __m128i xmm2, xmm3, msks; + for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) + { + if (szPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) + { + bool bFound = true; + for (uint8_t i = 1; i < iNumMasks; i++) + { + xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); + xmm3 = _mm_loadu_si128(reinterpret_cast((szPattern + i * 16))); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & nMasks[i]) != nMasks[i]) + { + bFound = false; + break; + } + } + if (bFound) + { + return static_cast((&*(const_cast(pData)))); + } + } + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find a string pattern in process memory using SIMD instructions +// Input : &svPattern +// &moduleSection +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const std::string_view svPattern, const ModuleSections_t* moduleSection) const +{ + const std::pair patternInfo = PatternToMaskedBytes(svPattern); + return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection); +} + +//----------------------------------------------------------------------------- +// Purpose: get the module section by name (example: '.rdata', '.text') +// Input : *svModuleName - +// Output : ModuleSections_t +//----------------------------------------------------------------------------- +const CModule::ModuleSections_t CModule::GetSectionByName(const std::string_view svSectionName) const +{ + for (const ModuleSections_t& section : m_vModuleSections) + { + if (section.m_svSectionName == svSectionName) + return section; + } + + return ModuleSections_t(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the module base +//----------------------------------------------------------------------------- +uintptr_t CModule::GetModuleBase(void) const +{ + return m_pModuleBase; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the module name +//----------------------------------------------------------------------------- +std::string_view CModule::GetModuleName(void) const +{ + return m_svModuleName; +} diff --git a/utils/module.h b/utils/module.h new file mode 100644 index 0000000..2d6e073 --- /dev/null +++ b/utils/module.h @@ -0,0 +1,50 @@ +#ifndef MODULE_H +#define MODULE_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include "memaddr.h" + +class CModule +{ +public: + struct ModuleSections_t + { + ModuleSections_t(void) = default; + ModuleSections_t(const std::string_view svSectionName, uintptr_t pSectionBase, size_t nSectionSize) : + m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) {} + + bool IsSectionValid(void) const + { + return m_nSectionSize != 0; + } + + std::string m_svSectionName; // Name of section. + uintptr_t m_pSectionBase{}; // Start address of section. + size_t m_nSectionSize{}; // Size of section. + }; + + CModule(const std::string_view moduleName); + CModule(const CMemory addr); + + CMemory FindPatternSIMD(const uint8_t* szPattern, const char* szMask, const ModuleSections_t* moduleSection = nullptr) const; + CMemory FindPatternSIMD(const std::string_view svPattern, const ModuleSections_t* moduleSection = nullptr) const; + + const ModuleSections_t GetSectionByName(const std::string_view svSectionName) const; + uintptr_t GetModuleBase(void) const; + std::string_view GetModuleName(void) const; + +private: + void Init(); + void LoadSections(); + + ModuleSections_t m_ExecutableCode; + std::string m_svModuleName; + uintptr_t m_pModuleBase{}; + std::vector m_vModuleSections; +}; + +#endif // MODULE_H