Compare commits

...

7 Commits

Author SHA1 Message Date
Nicholas Hastings
d4db62f199
Merge 1f39fc3265 into c4d5235217 2025-11-24 00:50:02 +01:00
caxanga334
c4d5235217
Update DoD:S Gamedata (#2377)
Some checks failed
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang, clang++, ubuntu-latest, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang-14, clang++-14, ubuntu-22.04, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (msvc, windows-latest, win) (push) Has been cancelled
hl2sdk-mock tests / mock (push) Has been cancelled
Adds missing GetAttachment vtable offset.
2025-11-20 23:43:49 +00:00
Benoist
6439769d50
Introduce Virtual Address (#2226)
Some checks failed
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang, clang++, ubuntu-latest, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang-14, clang++-14, ubuntu-22.04, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (msvc, windows-latest, win) (push) Has been cancelled
hl2sdk-mock tests / mock (push) Has been cancelled
SourcePawn scripting / build (ubuntu-latest, linux) (push) Has been cancelled
SourcePawn scripting / build (windows-latest, win) (push) Has been cancelled
In the current ongoing effort for sourcemod to fully support 64 bits, we are introducing "virtual address".

# Explanation

Because SourcePawn does not yet support a 64 bits-wide type it's been impossible for any plugins to hold addresses in regular 32-bits wide variable.

A first attempt at solving this issue was made in commit ce1a4dcac0 therein dubbed "PseudoAddress", however this turned out to be an unsatisfactory solution, as any 'high' address if offsetted could turn invalid (or outright be impossible to map).

This leaves us with three alternatives :
- New type
- Convert Address into a handle
- Virtual Address

A new type is the most destructive solution, as it entails breaking every single Address related method. While that solution is still not off the table, we're reserving it as the last attempt should this commit fail.

Converting into a handle type is a good compromise between a brand new type whilst also preserving the Address methods. However, this comes with two issues: the first being that you can no longer offset Address, the second is that we would require authors to free the handle type which will be very confusing. This will likely not be implemented.

# Virtual address

Under a reasonable assumption, we've noted that the average plugin is unlikely to play with more than 4 GB of memory; this shouldn't be too surprising as all valve games were once 32bits and therefore limited to 4GB. Assuming this stays mostly true and a plugin isn't interested with the mapped memory of lesser known modules (like soundlib or matlib), it is fair to assume plugins are unlikely to access more than 4GB of mapped memory. Working with this in mind, we map the memory the plugins are likely to access to our custom virtual address ranges (from 0 to 4Gb, the values of which can fit on 32bits variable). If any memory was missed and plugins were to try an access it later those ranges will be late-mapped to our virtual address ranges until we run out of them.

In order to use virtual addressing, whether on 32 bits or 64 bits. Plugins must now "#include <virtual_address>", as well as use the new SDKCall_VirtualAddress, SDKType_VirtualAddress, LoadAddressFromAddress & StoreAddressToAddress where it's appropriate to.
2025-11-13 13:23:13 +01:00
solidDoWant
0c900be7fc
Make PostgreSQL setup scripts idempotent (#2376)
Some checks failed
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang, clang++, ubuntu-latest, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (clang-14, clang++-14, ubuntu-22.04, linux) (push) Has been cancelled
Continuous Integration / ${{ matrix.os_short }}-${{ matrix.compiler_cc }} (msvc, windows-latest, win) (push) Has been cancelled
hl2sdk-mock tests / mock (push) Has been cancelled
Signed-off-by: solidDoWant <fred.heinecke@yahoo.com>
2025-11-10 15:39:08 +00:00
Nick Hastings
1f39fc3265 Disable existing CI job on master push 2024-07-07 19:05:01 -04:00
Nick Hastings
600bbbcb54 Switch to AM org for GHCR image pull 2024-07-07 11:29:25 -04:00
Nick Hastings
21fce47352 Add support for building real releases with GitHub Actions 2024-07-07 11:17:19 -04:00
25 changed files with 608 additions and 202 deletions

221
.github/workflows/build-release.yml vendored Normal file
View File

@ -0,0 +1,221 @@
name: Build master branch
on:
push:
branches:
- master
env:
ARCH: x86,x86_64
# Used for caching
# TODO: Handle this better so that we don't have to update this in lockstep with checkout-deps
MYSQL_VERSION: '5.5'
MMSOURCE_VERSION: '1.12'
jobs:
build:
permissions:
# For release creation, at the end
contents: write
strategy:
matrix:
include:
- platform: windows
os: windows-latest
os_short: win
- platform: linux
os: ubuntu-latest
os_short: linux
container_image: ghcr.io/alliedmodders/build-containers/debian11:latest
fail-fast: false
name: ${{ matrix.platform }}
runs-on: ${{ matrix.os }}
container:
image: ${{ matrix.container_image }}
steps:
- name: Checkout SourceMod
uses: actions/checkout@v4
with:
path: sourcemod
fetch-depth: 0
submodules: recursive
- name: Install Windows dependencies
if: startsWith(matrix.os, 'windows')
shell: pwsh
run: |
choco install -y gzip
choco install -y 7zip
# Add DIA SDK bin directory to PATH for dump_syms
Install-Module -Name VSSetup -Scope AllUsers -Force -AllowClobber
$vsRoot = Get-VSSetupInstance |
Sort-Object -Property InstallationVersion -Descending |
Select-Object -First 1 |
Select-Object -ExpandProperty InstallationPath
$diaDir = (Join-Path -Path $vsRoot -ChildPath 'DIA SDK\bin')
$diaDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
$toolsDir = New-Item -Path '_tools' -ItemType Directory -Force
$toolsDir.FullName | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Push-Location $toolsDir
# TODO: Move this somewhere more official
curl -sSL -o dump_syms.exe https://users.alliedmods.net/~psychonic/dump_syms.exe
Unblock-File -Path dump_syms.exe
Pop-Location
# Largely a workaround for issue where github.workspace doesn't match $GITHUB_WORKSPACE
# in containerized jobs. See https://github.com/actions/runner/issues/2058
- name: Resolve paths
id: path_helper
shell: bash
run: |
echo "dependencies=$GITHUB_WORKSPACE/dependencies" >> $GITHUB_OUTPUT
- name: Generate SDK list
id: sdk_list
shell: bash
run: |
sdk_list=()
for file in sourcemod/hl2sdk-manifests/manifests/*.json; do
sdk=$(basename "$file" .json)
if [[ $sdk == "mock" ]]; then
# We don't need to ship a build for the mock SDK
continue
fi
platform=$(jq -r '.platforms.${{ matrix.platform }}' "$file")
if [[ -z $platform || $platform == "null" || ${#platform[@]} -eq 0 ]]; then
continue
fi
source2=$(jq -r '.source2' "$file")
if [[ $source2 == "null" || $source2 == "true" ]]; then
continue
fi
sdk_list+=("$sdk")
done
echo "sdk_list=$(printf '%s\n' "${sdk_list[@]}" | jq --raw-input . | jq --slurp --compact-output .)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v4
env:
cache-name: hl2sdk-mysql-mmsource
with:
path: ${{ steps.path_helper.outputs.dependencies }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-${{ join(fromJson(steps.sdk_list.outputs.sdk_list), '') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-
${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-
- name: Install dependencies
shell: bash
run: |
mkdir -p '${{ steps.path_helper.outputs.dependencies }}'
cd '${{ steps.path_helper.outputs.dependencies }}'
# Satisfy checkout-deps requirement for a "sourcemod" folder.
mkdir -p sourcemod
../sourcemod/tools/checkout-deps.sh -s ${{ join(fromJson(steps.sdk_list.outputs.sdk_list), ',') }}
if [ ! -f GeoLite2-City_20191217/GeoLite2-City.mmdb ]; then
geotar="GeoLite2-City_20191217.tar.gz"
curl -sSL "https://sm.alliedmods.net/$geotar" -o $geotar
tar -xzf "$geotar"
rm "$geotar"
fi
- name: Build
working-directory: sourcemod
shell: bash
env:
BREAKPAD_SYMBOL_SERVER: ${{ vars.BREAKPAD_SYMBOL_SERVER }}
BREAKPAD_SYMBOL_SERVER_TOKEN: ${{ secrets.BREAKPAD_SYMBOL_SERVER_TOKEN }}
run: |
mkdir build
cd build
python3 ../configure.py \
--enable-optimize \
--breakpad-dump \
--no-color \
--symbol-files \
--sdks=${{ join(fromJson(steps.sdk_list.outputs.sdk_list), ',') }} \
--targets=${{ env.ARCH }} \
'--mms-path=${{ steps.path_helper.outputs.dependencies }}/mmsource-${{ env.MMSOURCE_VERSION }}' \
'--hl2sdk-root=${{ steps.path_helper.outputs.dependencies }}' \
'--mysql-path=${{ steps.path_helper.outputs.dependencies }}/mysql-${{ env.MYSQL_VERSION }}' \
'--mysql64-path=${{ steps.path_helper.outputs.dependencies }}/mysql-${{ env.MYSQL_VERSION }}-x86_64'
ambuild
mkdir -p addons/sourcemod/configs/geoip
cp '${{ steps.path_helper.outputs.dependencies }}/GeoLite2-City_20191217/GeoLite2-City.mmdb' addons/sourcemod/configs/geoip/GeoLite2-City.mmdb
- name: Package
id: package
working-directory: sourcemod/build/package
shell: bash
run: |
version_base=$(cat ../../product.version)
version_base=${version_base%-dev} # Ex. 1.12.0
version_rev=$(git rev-list --count HEAD)
version="${version_base}.${version_rev}"
expanded_version="${version_base}-git-${version_rev}"
if [ "${{ matrix.platform}}" == "windows" ]; then
filename="sourcemod-${expanded_version}-${{ matrix.platform }}.zip"
7z a "$filename" addons cfg
content_type="application/zip"
else
filename="sourcemod-${expanded_version}-${{ matrix.platform }}.tar.gz"
tar zcvf "$filename" addons cfg
content_type="application/gzip"
fi
echo "version=$version" >> $GITHUB_OUTPUT
echo "expanded_version=$expanded_version" >> $GITHUB_OUTPUT
echo "filename=$filename" >> $GITHUB_OUTPUT
echo "content_type=$content_type" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
uses: ncipollo/release-action@v1.14.0
with:
# Windows and Linux packages will need to upload to the same release
allowUpdates: true
replacesArtifacts: false
omitBodyDuringUpdate: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
artifacts: sourcemod/build/package/${{ steps.package.outputs.filename }}
artifactContentType: ${{ steps.package.outputs.content_type }}
artifactErrorsFailBuild: true
tag: ${{ steps.package.outputs.version }}
commit: ${{ github.sha }}
generateReleaseNotes: true
draft: false
prerelease: ${{ github.ref == 'refs/heads/master' }}
makeLatest: ${{ github.ref != 'refs/heads/master' }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Output PDBs
if: startsWith(matrix.os, 'windows')
shell: pwsh
id: output_pdbs
working-directory: sourcemod/build
run: |
$ErrorActionPreference = 'Stop'
$paths = (Get-Content -Path "pdblog.txt" | ForEach-Object -Process {
$fullRelPath = Join-Path -Path 'sourcemod/build' -ChildPath $_
$fullRelPath -replace '\.pdb$', '.*'
}) -join "`n"
"PDB_PATHS<<EOF" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$paths | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"EOF" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Upload PDBs
if: startsWith(matrix.os, 'windows')
uses: actions/upload-artifact@v4
with:
name: pdbs
path: ${{ env.PDB_PATHS }}

View File

@ -2,7 +2,6 @@ name: Continuous Integration
on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
pull_request:
branches:

3
.gitmodules vendored
View File

@ -12,3 +12,6 @@
[submodule "public/safetyhook"]
path = public/safetyhook
url = https://github.com/alliedmodders/safetyhook
[submodule "core/logic/libaddrz"]
path = core/logic/libaddrz
url = https://github.com/dvander/libaddrz.git

View File

@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS sm_cookie_cache
PRIMARY KEY (player, cookie_id)
);
CREATE LANGUAGE plpgsql;
CREATE OR REPLACE LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS
$$

View File

@ -1,4 +1,4 @@
CREATE TABLE sm_admins (
CREATE TABLE IF NOT EXISTS sm_admins (
id serial,
authtype varchar(6) NOT NULL,
CHECK (authtype in ('steam', 'name', 'ip')),
@ -10,7 +10,7 @@ CREATE TABLE sm_admins (
PRIMARY KEY (id)
);
CREATE TABLE sm_groups (
CREATE TABLE IF NOT EXISTS sm_groups (
id serial,
flags varchar(30) NOT NULL,
name varchar(120) NOT NULL,
@ -18,7 +18,7 @@ CREATE TABLE sm_groups (
PRIMARY KEY (id)
);
CREATE TABLE sm_group_immunity (
CREATE TABLE IF NOT EXISTS sm_group_immunity (
group_id int NOT NULL,
other_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
@ -26,7 +26,7 @@ CREATE TABLE sm_group_immunity (
PRIMARY KEY (group_id, other_id)
);
CREATE TABLE sm_group_overrides (
CREATE TABLE IF NOT EXISTS sm_group_overrides (
group_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
type varchar(10) NOT NULL,
@ -37,7 +37,7 @@ CREATE TABLE sm_group_overrides (
PRIMARY KEY (group_id, type, name)
);
CREATE TABLE sm_overrides (
CREATE TABLE IF NOT EXISTS sm_overrides (
type varchar(10) NOT NULL,
CHECK (type in ('command', 'group')),
name varchar(32) NOT NULL,
@ -45,7 +45,7 @@ CREATE TABLE sm_overrides (
PRIMARY KEY (type,name)
);
CREATE TABLE sm_admins_groups (
CREATE TABLE IF NOT EXISTS sm_admins_groups (
admin_id int NOT NULL,
group_id int NOT NULL,
FOREIGN KEY (admin_id) REFERENCES sm_admins(id) ON DELETE CASCADE,
@ -56,10 +56,10 @@ CREATE TABLE sm_admins_groups (
-- side note, this is pgsql module, sm_config will not exist if the above stuff exists... and it's being left to the admin
-- to figure out if it exists.
CREATE TABLE sm_config (
CREATE TABLE IF NOT EXISTS sm_config (
cfg_key varchar(32) NOT NULL,
cfg_value varchar(255) NOT NULL,
PRIMARY KEY (cfg_key)
);
INSERT INTO sm_config (cfg_key, cfg_value) VALUES ('admin_version', '1.0.0.1409');
INSERT INTO sm_config (cfg_key, cfg_value) VALUES ('admin_version', '1.0.0.1409') ON CONFLICT (cfg_key) DO UPDATE SET cfg_value = EXCLUDED.cfg_value;

View File

@ -86,9 +86,15 @@ for cxx in builder.targets:
'DatabaseConfBuilder.cpp',
'LumpManager.cpp',
'smn_entitylump.cpp',
'libaddrz/addrz.cpp',
'libaddrz/mapping.cpp',
'libaddrz/platform.cpp',
'libaddrz/proc_maps.cpp',
'PseudoAddrManager.cpp',
]
if binary.compiler.target.arch == 'x86_64':
binary.sources += ['PseudoAddrManager.cpp']
if binary.compiler.target.platform == 'linux':
binary.sources += ['libaddrz/platform_linux.cpp']
elif binary.compiler.target.platform == 'windows':
binary.sources += ['libaddrz/platform_windows.cpp']
SM.binaries += [builder.Add(binary)]

View File

@ -330,6 +330,13 @@ bool CPlugin::ReadInfo()
else
m_MaxClientsVar = nullptr;
if (base->FindPubvarByName("PointerSize", &idx) == SP_ERROR_NONE) {
sp_pubvar_t* var = nullptr;
if (base->GetPubvarByIndex(idx, &var) == SP_ERROR_NONE && var) {
*var->offs = sizeof(void*);
}
}
return true;
}

View File

@ -28,6 +28,7 @@
*/
#include "PseudoAddrManager.h"
#include <bridge/include/CoreProvider.h>
#ifdef PLATFORM_APPLE
#include <mach/mach.h>
#include <mach/vm_region.h>
@ -35,135 +36,88 @@
#ifdef PLATFORM_LINUX
#include <inttypes.h>
#endif
#ifdef PLATFORM_WINDOWS
#include <Psapi.h>
#endif
PseudoAddressManager::PseudoAddressManager() : m_NumEntries(0)
PseudoAddressManager::PseudoAddressManager() : m_dictionary(am::IPlatform::GetDefault())
{
}
// A pseudo address consists of a table index in the upper 6 bits and an offset in the
// lower 26 bits. The table consists of memory allocation base addresses.
void PseudoAddressManager::Initialize() {
#ifdef PLATFORM_WINDOWS
auto process = GetCurrentProcess();
auto get_module_details = [process](const char* name, void*& baseAddress, size_t& moduleSize) {
if (process == NULL) {
return false;
}
auto hndl = GetModuleHandle(name);
if (hndl == NULL) {
return false;
}
MODULEINFO info;
if (!GetModuleInformation(process, hndl, &info, sizeof(info))) {
return false;
}
moduleSize = info.SizeOfImage;
baseAddress = info.lpBaseOfDll;
return true;
};
#endif
#ifdef PLATFORM_LINUX
auto get_module_details = [](const char* name, void* baseAddress, size_t& moduleSize) {
auto hndl = dlopen(name, RTLD_NOLOAD);
if (hndl == NULL) {
return false;
}
void* addr = dlsym(hndl, "CreateInterface");
dlclose(hndl);
if (!addr) {
return false;
}
Dl_info info;
if (dladdr(addr, &info) == 0) {
return false;
}
baseAddress = info.dli_fbase;
// It doesn't matter much if we figure out the module size
// libaddrz coalesce maps on linux
moduleSize = 0;
return true;
};
#endif
// Early map commonly used modules, it's okay if not all of them are here
// Everything else will be caught by "ToPseudoAddress" but you risk running out of ranges by then
const char* libs[] = { "engine", "server", "tier0", "vstdlib" };
char formattedName[64];
for (int i = 0; i < sizeof(libs) / sizeof(const char*); i++) {
bridge->FormatSourceBinaryName(libs[i], formattedName, sizeof(formattedName));
void* base_addr = nullptr;
size_t module_size = 0;
if (get_module_details(formattedName, base_addr, module_size)) {
// Create the mapping (hopefully)
m_dictionary.Make32bitAddress(base_addr, module_size);
}
}
}
void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr)
{
#ifdef KE_ARCH_X64
uint8_t index = paddr >> PSEUDO_OFFSET_BITS;
uint32_t offset = paddr & ((1 << PSEUDO_OFFSET_BITS) - 1);
if (index >= m_NumEntries)
if (paddr == 0) {
return nullptr;
return reinterpret_cast<void *>(uintptr_t(m_AllocBases[index]) + offset);
#else
return nullptr;
#endif
}
return m_dictionary.RecoverAddress(paddr).value_or(nullptr);
}
uint32_t PseudoAddressManager::ToPseudoAddress(void *addr)
{
#ifdef KE_ARCH_X64
uint8_t index = 0;
uint32_t offset = 0;
bool hasEntry = false;
void *base = GetAllocationBase(addr);
if (base) {
for (int i = 0; i < m_NumEntries; i++) {
if (m_AllocBases[i] == base) {
index = i;
hasEntry = true;
break;
}
}
} else {
if (addr == nullptr) {
return 0;
}
if (!hasEntry) {
index = m_NumEntries;
if (m_NumEntries < SM_ARRAYSIZE(m_AllocBases))
m_AllocBases[m_NumEntries++] = base;
else
return 0; // Table is full
}
ptrdiff_t diff = uintptr_t(addr) - uintptr_t(base);
// Ensure difference fits in 26 bits
if (diff > (UINT32_MAX >> PSEUDO_INDEX_BITS))
return 0;
return (index << PSEUDO_OFFSET_BITS) | diff;
#else
return 0;
#endif
}
void *PseudoAddressManager::GetAllocationBase(void *ptr)
{
#if defined PLATFORM_WINDOWS
MEMORY_BASIC_INFORMATION info;
if (!VirtualQuery(ptr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
return nullptr;
return info.AllocationBase;
#elif defined PLATFORM_APPLE
#ifdef KE_ARCH_X86
typedef vm_region_info_t mach_vm_region_info_t;
typedef vm_region_basic_info_data_t mach_vm_region_basic_info_data_t;
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO;
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT;
#define mach_vm_region vm_region
#elif defined KE_ARCH_X64
typedef vm_region_info_64_t mach_vm_region_info_t ;
typedef vm_region_basic_info_data_64_t mach_vm_region_basic_info_data_t;
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO_64;
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT_64;
#define mach_vm_region vm_region_64
#endif
vm_size_t size;
vm_address_t vmaddr = reinterpret_cast<vm_address_t>(ptr);
mach_vm_region_basic_info_data_t info;
memory_object_name_t obj;
vm_region_flavor_t flavor = MACH_VM_REGION_BASIC_INFO;
mach_msg_type_number_t count = MACH_VM_REGION_BASIC_INFO_COUNT;
kern_return_t kr = mach_vm_region(mach_task_self(), &vmaddr, &size, flavor,
reinterpret_cast<mach_vm_region_info_t>(&info),
&count, &obj);
if (kr != KERN_SUCCESS)
return nullptr;
return reinterpret_cast<void *>(vmaddr);
#elif defined PLATFORM_LINUX
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
// Format:
// lower upper prot stuff path
// 08048000-0804c000 r-xp 00000000 03:03 1010107 /bin/cat
FILE *fp = fopen("/proc/self/maps", "r");
if (fp) {
uintptr_t lower, upper;
while (fscanf(fp, "%" PRIxPTR "-%" PRIxPTR, &lower, &upper) != EOF) {
if (addr >= lower && addr <= upper) {
fclose(fp);
return reinterpret_cast<void *>(lower);
}
// Read to end of line
int c;
while ((c = fgetc(fp)) != '\n') {
if (c == EOF)
break;
}
if (c == EOF)
break;
}
fclose(fp);
}
return nullptr;
#endif
return m_dictionary.Make32bitAddress(addr).value_or(0);
}

View File

@ -31,6 +31,7 @@
#define _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_
#include "common_logic.h"
#include "libaddrz/addrz.h"
class PseudoAddressManager
{
@ -39,13 +40,9 @@ public:
public:
void *FromPseudoAddress(uint32_t paddr);
uint32_t ToPseudoAddress(void *addr);
void Initialize();
private:
void *GetAllocationBase(void *ptr);
private:
static constexpr uint8_t PSEUDO_OFFSET_BITS = 26;
static constexpr uint8_t PSEUDO_INDEX_BITS = sizeof(uint32_t) * 8 - PSEUDO_OFFSET_BITS;
void *m_AllocBases[1 << PSEUDO_INDEX_BITS];
uint8_t m_NumEntries;
am::AddressDict m_dictionary;
};
#endif // _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_

View File

@ -57,6 +57,7 @@
#include "RootConsoleMenu.h"
#include "CellArray.h"
#include "smn_entitylump.h"
#include "PseudoAddrManager.h"
#include <bridge/include/BridgeAPI.h>
#include <bridge/include/IProviderCallbacks.h>
@ -86,9 +87,7 @@ IScriptManager *scripts = &g_PluginSys;
IExtensionSys *extsys = &g_Extensions;
ILogger *logger = &g_Logger;
CNativeOwner g_CoreNatives;
#ifdef KE_ARCH_X64
PseudoAddressManager pseudoAddr;
#endif
EntityLumpParseResult lastParseResult;
@ -122,20 +121,12 @@ static void RegisterProfiler(IProfilingTool *tool)
static void *FromPseudoAddress(uint32_t paddr)
{
#ifdef KE_ARCH_X64
return pseudoAddr.FromPseudoAddress(paddr);
#else
return nullptr;
#endif
}
static uint32_t ToPseudoAddress(void *addr)
{
#ifdef KE_ARCH_X64
return pseudoAddr.ToPseudoAddress(addr);
#else
return 0;
#endif
}
static void SetEntityLumpWritable(bool writable)
@ -236,6 +227,7 @@ static void logic_init(CoreProvider* core, sm_logic_t* _logic)
g_pSourcePawn2 = *core->spe2;
SMGlobalClass::head = core->listeners;
pseudoAddr.Initialize();
g_ShareSys.Initialize();
g_pCoreIdent = g_ShareSys.CreateCoreIdentity();

1
core/logic/libaddrz Submodule

@ -0,0 +1 @@
Subproject commit 661cd316e6ff8e8560efa20db1794f3fa479647c

View File

@ -59,6 +59,7 @@
#include <bridge/include/CoreProvider.h>
#include <bridge/include/IScriptManager.h>
#include <bridge/include/IExtensionBridge.h>
#include "PseudoAddrManager.h"
#include <sh_vector.h>
using namespace SourceMod;
@ -863,11 +864,10 @@ enum NumberType
static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params)
{
#ifdef KE_ARCH_X86
void *addr = reinterpret_cast<void*>(params[1]);
#else
void *addr = pseudoAddr.FromPseudoAddress(params[1]);
#endif
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = pseudoAddr.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
@ -892,14 +892,12 @@ static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params)
}
}
static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params)
{
#ifdef KE_ARCH_X86
void *addr = reinterpret_cast<void*>(params[1]);
#else
void *addr = pseudoAddr.FromPseudoAddress(params[1]);
#endif
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = pseudoAddr.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
@ -950,6 +948,60 @@ static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params)
return 0;
}
static cell_t LoadAddressFromAddress(IPluginContext *pContext, const cell_t *params)
{
void *addr = reinterpret_cast<void*>(params[1]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = pseudoAddr.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
return pContext->ThrowNativeError("Address cannot be null");
}
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
{
return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
}
void* data = *reinterpret_cast<void**>(addr);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return pseudoAddr.ToPseudoAddress(data);
}
return reinterpret_cast<uintptr_t>(data);
}
static cell_t StoreAddressToAddress(IPluginContext *pContext, const cell_t *params)
{
void *addr = reinterpret_cast<void*>(params[1]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = pseudoAddr.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
return pContext->ThrowNativeError("Address cannot be null");
}
else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
{
return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
}
void *data = reinterpret_cast<void*>(params[2]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
data = pseudoAddr.FromPseudoAddress(params[2]);
}
bool updateMemAccess = params[3];
if (updateMemAccess) {
SourceHook::SetMemAccess(addr, sizeof(void*), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
}
*reinterpret_cast<void**>(addr) = data;
return 0;
}
static cell_t IsNullVector(IPluginContext *pContext, const cell_t *params)
{
cell_t *pNullVec = pContext->GetNullRef(SP_NULL_VECTOR);
@ -1157,6 +1209,8 @@ REGISTER_NATIVES(coreNatives)
{"RequireFeature", RequireFeature},
{"LoadFromAddress", LoadFromAddress},
{"StoreToAddress", StoreToAddress},
{"LoadAddressFromAddress", LoadAddressFromAddress},
{"StoreAddressToAddress", StoreAddressToAddress},
{"IsNullVector", IsNullVector},
{"IsNullString", IsNullString},
{"LogStackTrace", LogStackTrace},

View File

@ -32,6 +32,7 @@
#include "common_logic.h"
#include <IHandleSys.h>
#include "GameConfigs.h"
#include "PseudoAddrManager.h"
HandleType_t g_GameConfigsType;
@ -155,11 +156,10 @@ static cell_t smn_GameConfGetAddress(IPluginContext *pCtx, const cell_t *params)
if (!gc->GetAddress(key, &val))
return 0;
#ifdef KE_ARCH_X86
return (cell_t)val;
#else
return pseudoAddr.ToPseudoAddress(val);
#endif
if (pCtx->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return pseudoAddr.ToPseudoAddress(val);
}
return reinterpret_cast<uintptr_t>(val);
}
static cell_t smn_GameConfGetMemSig(IPluginContext *pCtx, const cell_t *params)
@ -187,11 +187,10 @@ static cell_t smn_GameConfGetMemSig(IPluginContext *pCtx, const cell_t *params)
return 0;
}
#ifdef KE_ARCH_X86
return (cell_t)val;
#else
return pseudoAddr.ToPseudoAddress(val);
#endif
if (pCtx->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return pseudoAddr.ToPseudoAddress(val);
}
return reinterpret_cast<uintptr_t>(val);
}
static GameConfigsNatives s_GameConfigsNatives;

View File

@ -724,11 +724,10 @@ static cell_t GetEntDataEnt2(IPluginContext *pContext, const cell_t *params)
static cell_t LoadEntityFromHandleAddress(IPluginContext *pContext, const cell_t *params)
{
#ifdef KE_ARCH_X86
void *addr = reinterpret_cast<void*>(params[1]);
#else
void *addr = g_SourceMod.FromPseudoAddress(params[1]);
#endif
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = g_SourceMod.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
@ -835,11 +834,10 @@ static cell_t SetEntDataEnt2(IPluginContext *pContext, const cell_t *params)
static cell_t StoreEntityToHandleAddress(IPluginContext *pContext, const cell_t *params)
{
#ifdef KE_ARCH_X86
void *addr = reinterpret_cast<void*>(params[1]);
#else
void *addr = g_SourceMod.FromPseudoAddress(params[1]);
#endif
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = g_SourceMod.FromPseudoAddress(params[1]);
}
if (addr == NULL)
{
@ -2772,11 +2770,10 @@ static cell_t GetEntityAddress(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]);
}
#ifdef KE_ARCH_X86
return reinterpret_cast<cell_t>(pEntity);
#else
return g_SourceMod.ToPseudoAddress(pEntity);
#endif
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return g_SourceMod.ToPseudoAddress(pEntity);
}
return reinterpret_cast<uintptr_t>(pEntity);
}
REGISTER_NATIVES(entityNatives)

View File

@ -376,7 +376,7 @@ ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour)
{
// The this pointer is implicitly always the first argument.
void *thisPtr = pDetour->GetArgument<void *>(0);
cell_t thisAddr = GetThisPtr(thisPtr, pWrapper->thisType);
cell_t thisAddr = GetThisPtr(pCallback->GetParentContext(), thisPtr, pWrapper->thisType);
pCallback->PushCell(thisAddr);
}

View File

@ -174,7 +174,12 @@ cell_t Native_CreateHook(IPluginContext *pContext, const cell_t *params)
//native Handle:DHookCreateDetour(Address:funcaddr, CallingConvention:callConv, ReturnType:returntype, ThisPointerType:thistype);
cell_t Native_CreateDetour(IPluginContext *pContext, const cell_t *params)
{
HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], (void *)params[1]);
void* addr = reinterpret_cast<void*>(params[1]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
addr = g_pSM->FromPseudoAddress(params[1]);
}
HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], addr);
Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL);
@ -641,7 +646,10 @@ cell_t HookRawImpl(IPluginContext *pContext, const cell_t *params, int callbackI
if (removalcbIndex > 0)
removalcb = pContext->GetFunctionById(params[removalcbIndex]);
void *iface = (void *)(params[3]);
void* iface = reinterpret_cast<void*>(params[3]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
iface = g_pSM->FromPseudoAddress(params[3]);
}
for(int i = g_pHooks.size() -1; i >= 0; i--)
{
@ -1482,6 +1490,10 @@ cell_t Native_GetParamAddress(IPluginContext *pContext, const cell_t *params)
}
size_t offset = GetParamOffset(paramStruct, index);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return g_pSM->ToPseudoAddress(*(void**)((intptr_t)paramStruct->orgParams + offset));
}
return *(cell_t *)((intptr_t)paramStruct->orgParams + offset);
}

View File

@ -465,19 +465,18 @@ HookReturnStruct *GetReturnStruct(DHooksCallback *dg)
return res;
}
cell_t GetThisPtr(void *iface, ThisPointerType type)
cell_t GetThisPtr(IPluginContext* pContext, void *iface, ThisPointerType type)
{
if(type == ThisPointer_CBaseEntity)
if (type == ThisPointer_CBaseEntity)
{
if (!iface)
return -1;
return gamehelpers->EntityToBCompatRef((CBaseEntity *)iface);
}
#ifdef KE_ARCH_X64
return g_pSM->ToPseudoAddress(iface);
#else
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return g_pSM->ToPseudoAddress(iface);
}
return (cell_t)iface;
#endif
}
#if defined( WIN32 ) && !defined( KE_ARCH_X64 )
@ -500,7 +499,7 @@ void *Callback(DHooksCallback *dg, void **argStack)
if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address)
{
dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType));
dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType));
}
if(dg->returnType != ReturnType_Void)
{
@ -684,7 +683,7 @@ float Callback_float(DHooksCallback *dg, void **argStack)
if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address)
{
dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType));
dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType));
}
returnStruct = GetReturnStruct(dg);
@ -841,7 +840,7 @@ SDKVector *Callback_vector(DHooksCallback *dg, void **argStack)
if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address)
{
dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType));
dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType));
}
returnStruct = GetReturnStruct(dg);
@ -995,7 +994,7 @@ string_t *Callback_stringt(DHooksCallback *dg, void **argStack)
if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address)
{
dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType));
dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType));
}
returnStruct = GetReturnStruct(dg);

View File

@ -339,7 +339,7 @@ public:
};
size_t GetStackArgsSize(DHooksCallback *dg);
cell_t GetThisPtr(void *iface, ThisPointerType type);
cell_t GetThisPtr(IPluginContext* pContext, void *iface, ThisPointerType type);
extern IBinTools *g_pBinTools;
extern HandleType_t g_HookParamsHandle;

View File

@ -63,7 +63,8 @@ inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &ty
type = PassType_Basic;
if (vtype == Valve_POD
|| vtype == Valve_Float
|| vtype == Valve_Bool)
|| vtype == Valve_Bool
|| vtype == Valve_VirtualAddress)
{
flags = PASSFLAG_BYVAL | PASSFLAG_ASPOINTER;
} else {
@ -86,6 +87,13 @@ inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &ty
static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
{
auto call_type = (ValveCallType)params[1];
if (call_type == ValveCall_Raw && pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
return pContext->ThrowNativeError("SDKCall_Raw is unavailable for plugins that have enabled virtual address.");
}
if (call_type == ValveCall_VirtualAddress && pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) != SP_ERROR_NONE) {
return pContext->ThrowNativeError("SDKCall_VirtualAddress is unavailable for plugins that haven't enabled virtual address.");
}
s_numparams = 0;
s_vtbl_index = -1;
s_call_addr = NULL;
@ -172,11 +180,10 @@ static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *p
static cell_t PrepSDKCall_SetAddress(IPluginContext *pContext, const cell_t *params)
{
#ifdef KE_ARCH_X86
s_call_addr = reinterpret_cast<void *>(params[1]);
#else
s_call_addr = g_pSM->FromPseudoAddress(params[1]);
#endif
s_call_addr = reinterpret_cast<void*>(params[1]);
if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) {
s_call_addr = g_pSM->FromPseudoAddress(params[1]);
}
return (s_call_addr != NULL) ? 1 : 0;
}
@ -413,6 +420,36 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
startparam++;
}
break;
case ValveCall_VirtualAddress:
{
//params[startparam] is an address to a pointer to THIS
//params following this are params to the method we will invoke later
if (startparam > numparams)
{
vc->stk_put(ptr);
return pContext->ThrowNativeError("Expected a ThisPtr address, it wasn't found");
}
//note: varargs pawn args are passed by-ref
cell_t *cell;
pContext->LocalToPhysAddr(params[startparam], &cell);
void* thisptr = reinterpret_cast<void*>(g_pSM->FromPseudoAddress(*cell));
if (thisptr == nullptr)
{
vc->stk_put(ptr);
return pContext->ThrowNativeError("ThisPtr address cannot be null");
}
else if (reinterpret_cast<uintptr_t>(thisptr) < VALID_MINIMUM_MEMORY_ADDRESS)
{
vc->stk_put(ptr);
return pContext->ThrowNativeError("Invalid ThisPtr address %p is pointing to reserved memory.", thisptr);
}
*(void **)ptr = thisptr;
startparam++;
}
break;
default:
{
vc->stk_put(ptr);
@ -536,6 +573,13 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
addr = *(bool **)addr;
}
return *addr ? 1 : 0;
} else if (vc->retinfo->vtype == Valve_VirtualAddress) {
void *addr = *(void **)vc->retbuf;
if (vc->retinfo->flags & PASSFLAG_ASPOINTER)
{
addr = *(void **)addr;
}
return g_pSM->ToPseudoAddress(addr);
} else {
cell_t *addr = (cell_t *)vc->retbuf;
if (vc->retinfo->flags & PASSFLAG_ASPOINTER)

View File

@ -164,6 +164,21 @@ size_t ValveParamToBinParam(ValveType type,
return sizeof(float);
}
}
case Valve_VirtualAddress:
{
info->flags = flags;
if (flags & PASSFLAG_ASPOINTER)
{
needs_extra = true;
info->type = PassType_Basic;
info->size = sizeof(void**);
return sizeof(void**) + sizeof(void*);
} else {
info->type = PassType_Basic;
info->size = sizeof(void*);
return sizeof(void*);
}
}
}
return 0;
@ -276,6 +291,19 @@ DataStatus EncodeValveParam(IPluginContext *pContext,
*addr = *(bool *)buffer ? 1 : 0;
return Data_Okay;
}
case Valve_VirtualAddress:
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
if (data->flags & PASSFLAG_ASPOINTER)
{
buffer = *(void **)buffer;
}
*addr = g_pSM->ToPseudoAddress(*(void**)buffer);
return Data_Okay;
}
}
@ -585,6 +613,29 @@ DataStatus DecodeValveParam(IPluginContext *pContext,
*(char **)buffer = addr;
return Data_Okay;
}
case Valve_VirtualAddress:
{
if (data->decflags & VDECODE_FLAG_BYREF)
{
cell_t *addr;
pContext->LocalToPhysAddr(param, &addr);
param = *addr;
}
if (data->flags & PASSFLAG_ASPOINTER)
{
*(void **)buffer = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
buffer = *(void **)buffer;
}
void* addr = g_pSM->FromPseudoAddress(param);
if (addr == nullptr && (data->decflags & VDECODE_FLAG_ALLOWNULL) == 0)
{
pContext->ThrowNativeError("NULL Address not allowed");
return Data_Fail;
}
*(void **)buffer = addr;
return Data_Okay;
}
}
return Data_Fail;

View File

@ -53,6 +53,7 @@ enum ValveType
Valve_Edict, /**< Edict */
Valve_String, /**< String */
Valve_Bool, /**< Boolean */
Valve_VirtualAddress, /**< SM Virtual Address */
Valve_Object, /**< Object, not matching one of the above types */
};
@ -84,6 +85,7 @@ enum ValveCallType
ValveCall_Raw, /**< Thiscall (address explicit first parameter) */
ValveCall_Server, /**< Thiscall (CBaseServer implicit first parameter) */
ValveCall_Engine, /**< Thiscall (CVEngineServer implicit first parameter) */
ValveCall_VirtualAddress /**< Thiscall (address explicit first parameter) */
};
/**

View File

@ -121,6 +121,13 @@
"linux" "259"
"linux64" "259"
}
"GetAttachment"
{
"windows" "211"
"windows64" "211"
"linux" "212"
"linux64" "212"
}
}
"Keys"

View File

@ -60,9 +60,10 @@ enum SDKCallType
SDKCall_Player, /**< CBasePlayer call */
SDKCall_GameRules, /**< CGameRules call */
SDKCall_EntityList, /**< CGlobalEntityList call */
SDKCall_Raw, /**< |this| pointer with an arbitrary address */
SDKCall_Raw, /**< |this| pointer with an arbitrary address. This is not available if SM's virtual addresses are enabled */
SDKCall_Server, /**< CBaseServer call */
SDKCall_Engine /**< CVEngineServer call */
SDKCall_Engine, /**< CVEngineServer call */
SDKCall_VirtualAddress /**< |this| pointer with an arbitrary SM virtual address */
};
enum SDKLibrary
@ -88,7 +89,8 @@ enum SDKType
SDKType_Float, /**< Float (any) */
SDKType_Edict, /**< edict_t (always as pointer) */
SDKType_String, /**< NULL-terminated string (always as pointer) */
SDKType_Bool /**< Boolean (any) */
SDKType_Bool, /**< Boolean (any) */
SDKType_VirtualAddress, /**< SM Virtual Address */
};
enum SDKPassMethod

View File

@ -754,6 +754,26 @@ native any LoadFromAddress(Address addr, NumberType size);
*/
native void StoreToAddress(Address addr, any data, NumberType size, bool updateMemAccess = true);
/**
* Load sizeof(void*) from a memory address.
*
* @param addr Address to a memory location.
* @return The address that is stored at that address.
* @error Address is null or pointing to reserved memory.
*/
native Address LoadAddressFromAddress(Address addr);
/**
* Store sizeof(void*) bytes to a memory address.
*
* @param addr Address to a memory location.
* @param data Address to store at that location.
* @param updateMemAccess If true, SourceMod will set read / write / exec permissions
* on the memory page being written to.
* @error Address is null or pointing to reserved memory.
*/
native void StoreAddressToAddress(Address addr, Address data, bool updateMemAccess = true);
methodmap FrameIterator < Handle {
// Creates a stack frame iterator to build your own stack traces.
// @return New handle to a FrameIterator.

View File

@ -0,0 +1,39 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod (C)2004-2025 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This file is part of the SourceMod/SourcePawn SDK.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#if defined _virtual_address_included
#endinput
#endif
#define _virtual_address_included
public const Address __Virtual_Address__ = view_as<Address>(0);
public const Address PointerSize = view_as<Address>(0); /**< Size of a pointer in bytes (same as `sizeof(void*)`) */