Merge branch 'alliedmodders:master' into ext-yyjson

This commit is contained in:
ProjectSky 2025-11-16 10:29:43 +08:00 committed by GitHub
commit 0ad8d15d1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 933 additions and 438 deletions

View File

@ -54,9 +54,9 @@ jobs:
# Setup Python for AMBuild
- uses: actions/setup-python@v6
name: Setup Python 3.8
name: Setup Python 3.12
with:
python-version: 3.8
python-version: 3.12
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel

View File

@ -31,9 +31,9 @@ jobs:
# Setup Python for AMBuild
- uses: actions/setup-python@v6
name: Setup Python 3.8
name: Setup Python 3.12
with:
python-version: 3.8
python-version: 3.12
- name: Install AMBuild
run: |
python -m pip install --upgrade pip setuptools wheel
@ -67,7 +67,7 @@ jobs:
echo "SM_VERSION=$(cat ../product.version)" >> $GITHUB_ENV
- name: Archive tooling
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: sourcemod-tooling-${{ env.SM_VERSION }}-${{ matrix.os_short }}
path: build/package

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

@ -58,6 +58,7 @@ public:
virtual bool IsDirectory(const char *pFileName, const char *pathID = 0) = 0;
virtual void CreateDirHierarchy(const char *path, const char *pathID = 0) = 0;
virtual int GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, int nMaxLen) = 0;
virtual const char * GetGameBinArchSubdirectory() = 0;
};
} // namespace SourceMod

View File

@ -21,6 +21,15 @@
*/
"LogMode" "daily"
/**
* This option determines the time format SourceMod logging should use.
*
* "default" - Uses SourceMod's default time format. (%m/%d/%Y - %H:%M:%S)
* You can specify any time format you want. See https://cplusplus.com/reference/ctime/strftime/ for a list of format parameters.
* Example: "%d/%m/%Y - %H:%M:%S"
*/
"LogTimeFormat" "default"
/**
* Language that multilingual enabled plugins and extensions will use to print messages.
* Only languages listed in languages.cfg are valid.

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

@ -1094,13 +1094,18 @@ void GameBinPathManager::Init()
std::istringstream iss(search_path);
for (std::string path; std::getline(iss, path, ';');)
{
if (path.length() > 0
&& path.find(addons_folder) == std::string::npos
&& m_lookup.find(path) == m_lookup.cend()
)
if (path.length() > 0)
{
m_lookup.insert(path);
m_ordered.push_back(path);
const char* arch_subdir = bridge->filesystem->GetGameBinArchSubdirectory();
std::string full_path = path + arch_subdir;
if (full_path.find(addons_folder) == std::string::npos
&& m_lookup.find(full_path) == m_lookup.cend()
)
{
m_lookup.insert(full_path);
m_ordered.push_back(full_path);
}
}
}

View File

@ -29,6 +29,7 @@
* Version: $Id$
*/
#include <string_view>
#include <time.h>
#include <cstdarg>
#include "Logger.h"
@ -81,6 +82,19 @@ ConfigResult Logger::OnSourceModConfigChanged(const char *key,
return ConfigResult_Reject;
}
return ConfigResult_Accept;
} else if (strcasecmp(key, "LogTimeFormat") == 0) {
if (strcasecmp(value, "default") == 0)
{
m_isUsingDefaultTimeFormat = true;
m_UserTimeFormat.clear();
}
else {
// value is the time format string
m_isUsingDefaultTimeFormat = false;
m_UserTimeFormat.assign(value);
}
return ConfigResult_Accept;
}
@ -152,11 +166,7 @@ void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
char buffer[3072];
ke::SafeVsprintf(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
const char* date = GetFormattedDate();
fprintf(fp, "L %s: %s\n", date, buffer);
if (!sv_logecho || bridge->GetCvarBool(sv_logecho))
@ -174,10 +184,7 @@ void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
char buffer[3072];
ke::SafeVsprintf(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
const char* date = GetFormattedDate();
fprintf(fp, "L %s: %s\n", date, buffer);
fflush(fp);
@ -378,11 +385,7 @@ FILE *Logger::_OpenNormal()
if (!m_DamagedNormalFile)
{
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
const char* date = GetFormattedDate();
fprintf(pFile, "L %s: SourceMod log file session started (file \"%s\") (Version \"%s\")\n", date, m_NormalFileName.c_str(), SOURCEMOD_VERSION);
m_DamagedNormalFile = true;
}
@ -403,11 +406,7 @@ FILE *Logger::_OpenError()
if (!m_DamagedErrorFile)
{
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
const char* date = GetFormattedDate();
fprintf(pFile, "L %s: SourceMod error session started\n", date);
fprintf(pFile, "L %s: Info (map \"%s\") (file \"%s\")\n", date, m_CurrentMapName.c_str(), m_ErrorFileName.c_str());
m_DamagedErrorFile = true;
@ -452,3 +451,25 @@ void Logger::_CloseError()
void Logger::_CloseFatal()
{
}
const char* Logger::GetFormattedDate() const
{
static char date[256];
constexpr std::string_view DEFAULT_TIME_FORMAT{ "%m/%d/%Y - %H:%M:%S" };
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
if (m_isUsingDefaultTimeFormat)
{
strftime(date, sizeof(date), DEFAULT_TIME_FORMAT.data(), curtime);
}
else
{
strftime(date, sizeof(date), m_UserTimeFormat.c_str(), curtime);
}
return date;
}

View File

@ -53,7 +53,7 @@ enum LoggingMode
class Logger : public SMGlobalClass, public ILogger
{
public:
Logger() : m_Day(-1), m_Mode(LoggingMode_Daily), m_Active(true), m_DamagedNormalFile(false), m_DamagedErrorFile(false)
Logger() : m_Day(-1), m_Mode(LoggingMode_Daily), m_Active(true), m_DamagedNormalFile(false), m_DamagedErrorFile(false), m_isUsingDefaultTimeFormat(true)
{
}
public: //SMGlobalClass
@ -95,10 +95,12 @@ private:
void _LogFatalOpen(std::string &str);
void _PrintToGameLog(const char *fmt, va_list ap);
void _UpdateFiles(bool bLevelChange = false);
const char* GetFormattedDate() const;
private:
std::string m_NormalFileName;
std::string m_ErrorFileName;
std::string m_CurrentMapName;
std::string m_UserTimeFormat;
int m_Day;
@ -106,6 +108,7 @@ private:
bool m_Active;
bool m_DamagedNormalFile;
bool m_DamagedErrorFile;
bool m_isUsingDefaultTimeFormat;
};
extern Logger g_Logger;

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

@ -587,27 +587,68 @@ static cell_t FindStringInArray(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
// the blocknumber is not guaranteed to always be passed
// The parameters above 2 are optional
size_t blocknumber = 0;
if (params[0] >= 3)
{
blocknumber = (size_t)params[3];
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
}
if (blocknumber >= array->blocksize())
int startidx = -1;
if (params[0] >= 4)
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
startidx = params[4];
if (startidx > 0 && startidx > array->size())
{
return pContext->ThrowNativeError("Invalid start index %d (max: %d)", startidx, array->size());
}
}
bool reverse = false;
if (params[0] >= 5)
{
reverse = params[5];
}
typedef int (*STRCOMPARE)(const char *, const char *);
STRCOMPARE comparefn = (params[0] < 6 || params[6]) ? strcmp : strcasecmp;
char *str;
pContext->LocalToString(params[2], &str);
for (unsigned int i = 0; i < array->size(); i++)
if (reverse)
{
const char *array_str = (const char *)&array->base()[i * array->blocksize() + blocknumber];
if (strcmp(str, array_str) == 0)
if (startidx < 0)
{
return (cell_t) i;
startidx = array->size();
}
for (int i = (startidx - 1); i >= 0; i--)
{
const char *array_str = (const char *)&array->base()[i * array->blocksize() + blocknumber];
if (comparefn(str, array_str) == 0)
{
return (cell_t) i;
}
}
}
else
{
if (startidx < -1)
{
startidx = -1;
}
for (unsigned int i = (startidx + 1); i < array->size(); i++)
{
const char *array_str = (const char *)&array->base()[i * array->blocksize() + blocknumber];
if (comparefn(str, array_str) == 0)
{
return (cell_t) i;
}
}
}
@ -626,24 +667,62 @@ static cell_t FindValueInArray(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
// the blocknumber is not guaranteed to always be passed
// The parameters above 2 are optional
size_t blocknumber = 0;
if (params[0] >= 3)
{
blocknumber = (size_t) params[3];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
for (unsigned int i = 0; i < array->size(); i++)
{
cell_t *blk = array->at(i);
if (params[2] == blk[blocknumber])
blocknumber = (size_t)params[3];
if (blocknumber >= array->blocksize())
{
return (cell_t) i;
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
}
int startidx = -1;
if (params[0] >= 4)
{
startidx = params[4];
if (startidx > 0 && startidx > array->size())
{
return pContext->ThrowNativeError("Invalid start index %d (max: %d)", startidx, array->size());
}
}
bool reverse = false;
if (params[0] >= 5)
{
reverse = params[5];
}
if (reverse)
{
if (startidx < 0)
{
startidx = array->size();
}
for (int i = (startidx - 1); i >= 0; i--)
{
cell_t *blk = array->at(i);
if (params[2] == blk[blocknumber])
{
return (cell_t) i;
}
}
}
else
{
if (startidx < -1)
{
startidx = -1;
}
for (unsigned int i = (startidx + 1); i < array->size(); i++)
{
cell_t *blk = array->at(i);
if (params[2] == blk[blocknumber])
{
return (cell_t) i;
}
}
}

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

@ -31,6 +31,7 @@
#include <am-float.h>
#include <am-string.h>
#include <IDBDriver.h>
#include <IGameHelpers.h>
#include <ITranslator.h>
#include <bridge/include/IScriptManager.h>
#include <bridge/include/CoreProvider.h>
@ -188,6 +189,15 @@ bool AddString(char **buf_p, size_t &maxlen, const char *string, int width, int
width -= size;
if (!(flags & LADJUST))
{
while ((width-- > 0) && maxlen)
{
*buf++ = ' ';
maxlen--;
}
}
if (g_FormatEscapeDatabase && (flags & NOESCAPE) == 0)
{
char *tempBuffer = NULL;
@ -226,10 +236,13 @@ bool AddString(char **buf_p, size_t &maxlen, const char *string, int width, int
}
}
while ((width-- > 0) && maxlen)
if (flags & LADJUST)
{
*buf++ = ' ';
maxlen--;
while ((width-- > 0) && maxlen)
{
*buf++ = ' ';
maxlen--;
}
}
*buf_p = buf;
@ -254,6 +267,12 @@ void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, in
return;
}
if (ke::IsInfinite(static_cast<float>(fval)))
{
AddString(buf_p, maxlen, "Inf", width, prec, flags | NOESCAPE);
return;
}
// default precision
if (prec < 0)
{
@ -485,13 +504,15 @@ void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
unsignedVal /= 10;
} while (unsignedVal);
if (signedVal < 0)
{
text[digits++] = '-';
}
buf = *buf_p;
// minus sign BEFORE left padding if padding with zeros
if (signedVal < 0 && maxlen && (flags & ZEROPAD))
{
*buf++ = '-';
maxlen--;
}
if (!(flags & LADJUST))
{
while ((digits < width) && maxlen)
@ -502,6 +523,13 @@ void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
}
}
// minus sign AFTER left padding if padding with spaces
if (signedVal < 0 && maxlen && !(flags & ZEROPAD))
{
*buf++ = '-';
maxlen--;
}
while (digits-- && maxlen)
{
*buf++ = text[digits];
@ -1199,6 +1227,22 @@ reswitch:
arg++;
break;
}
case 'E':
{
CHECK_ARGS(0);
cell_t *value;
pCtx->LocalToPhysAddr(params[arg], &value);
CBaseEntity *entity = gamehelpers->ReferenceToEntity(*value);
if (!entity)
return pCtx->ThrowNativeError("Entity index %d is invalid (arg %d)", *value, arg);
const char *classname = gamehelpers->GetEntityClassname(entity);
if (!AddString(&buf_p, llen, classname, width, prec, flags))
return pCtx->ThrowNativeError("Escaped string would be truncated (arg %d)", arg);
arg++;
break;
}
case 's':
{
CHECK_ARGS(0);

View File

@ -194,6 +194,25 @@ public:
{
return filesystem->GetSearchPath(pathID, bGetPackFiles, pPath, nMaxLen);
}
const char* GetGameBinArchSubdirectory() override
{
#if defined( KE_ARCH_X64 ) && SOURCE_ENGINE >= SE_BLADE
#if defined( PLATFORM_WINDOWS )
#if SOURCE_ENGINE == SE_MCV
return "win64" PLATFORM_SEP;
#else
return "x64" PLATFORM_SEP;
#endif // SOURCE_ENGINE == SE_MCV
#elif defined( PLATFORM_LINUX )
return "linux64" PLATFORM_SEP;
#else
#error "Unsupported platform"
#endif // PLATFORM
#else
// Already included in the GameBin path(s), if required
return "";
#endif // defined( KE_ARCH_X64 ) && SOURCE_ENGINE >= SE_BLADE
}
} fs_wrapper;
class VPlayerInfo_Logic : public IPlayerInfoBridge

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

@ -30,8 +30,12 @@
#include "sourcemod.h"
#include "sourcemm_api.h"
#ifndef VPROF_ENABLED
#define VPROF_ENABLED
#endif
#ifndef RAD_TELEMETRY_DISABLED
#define RAD_TELEMETRY_DISABLED
#endif
#include <tier0/vprof.h>
VProfTool sVProfTool;

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);
}
@ -532,6 +532,7 @@ CDynamicHooksSourcePawn::CDynamicHooksSourcePawn(HookSetup *setup, CHook *pDetou
this->hookType = setup->hookType;
this->m_pDetour = pDetour;
this->callConv = setup->callConv;
this->thisFuncCallConv = setup->callConv;
}
HookReturnStruct *CDynamicHooksSourcePawn::GetReturnStruct()

View File

@ -42,6 +42,8 @@ enum SDKFuncConfSource
SDKConf_Address
};
using ParamVector = SourceHook::CVector<ParamInfo>;
bool GetHandleIfValidOrError(HandleType_t type, void **object, IPluginContext *pContext, cell_t param)
{
if(param == BAD_HANDLE)
@ -81,6 +83,56 @@ bool GetCallbackArgHandleIfValidOrError(HandleType_t type, HandleType_t otherTyp
return true;
}
bool GetObjectAddrOrThis(IPluginContext *pContext, const cell_t *params, void *&retAddr)
{
HookParamsStruct *paramStruct = NULL;
retAddr = NULL;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
{
return false;
}
if(params[2] != 0)
{
const ParamVector &paramsVec = paramStruct->dg->params;
if(params[2] < 0 || params[2] > static_cast<int>(paramsVec.size()))
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramsVec.size());
}
int index = params[2] - 1;
const ParamInfo &param = paramsVec.at(index);
if(param.type != HookParamType_ObjectPtr && param.type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", param.type);
}
size_t offset = GetParamOffset(paramStruct, index);
retAddr = GetObjectAddr(param.type, param.flags, paramStruct->orgParams, offset);
return true;
}
const DHooksInfo* dgInfo = paramStruct->dg;
if(dgInfo->thisFuncCallConv != CallConv_THISCALL)
{
return pContext->ThrowNativeError("Parameter 'this' is only available in member functions");
}
if(dgInfo->thisType != ThisPointer_Address
&& dgInfo->thisType != ThisPointer_CBaseEntity
&& dgInfo->hookType != HookType_GameRules)
{
return pContext->ThrowNativeError("Parameter 'this' is not specified as an address, it is not available");
}
retAddr = g_SHPtr->GetIfacePtr();
return true;
}
IPluginFunction *GetCallback(IPluginContext *pContext, HookSetup * setup, const cell_t *params, cell_t callback_index)
{
IPluginFunction *ret = NULL;
@ -122,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);
@ -589,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--)
{
@ -1089,28 +1149,12 @@ cell_t Native_RemoveEntityListener(IPluginContext *pContext, const cell_t *param
//native any:DHookGetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type);
cell_t Native_GetParamObjectPtrVar(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
void *addr = NULL;
if(!GetObjectAddrOrThis(pContext, params, addr))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_Int:
@ -1160,28 +1204,12 @@ cell_t Native_GetParamObjectPtrVar(IPluginContext *pContext, const cell_t *param
//native DHookSetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type, value)
cell_t Native_SetParamObjectPtrVar(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
void *addr = NULL;
if(!GetObjectAddrOrThis(pContext, params, addr))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_Int:
@ -1245,28 +1273,12 @@ cell_t Native_SetParamObjectPtrVar(IPluginContext *pContext, const cell_t *param
//native DHookGetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:buffer[3]);
cell_t Native_GetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
void *addr = NULL;
if(!GetObjectAddrOrThis(pContext, params, addr))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
cell_t *buffer;
pContext->LocalToPhysAddr(params[5], &buffer);
@ -1299,28 +1311,12 @@ cell_t Native_GetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t
//native DHookSetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:value[3]);
cell_t Native_SetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
void *addr = NULL;
if(!GetObjectAddrOrThis(pContext, params, addr))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
cell_t *buffer;
pContext->LocalToPhysAddr(params[5], &buffer);
@ -1352,28 +1348,12 @@ cell_t Native_SetParamObjectPtrVarVector(IPluginContext *pContext, const cell_t
//native DHookGetParamObjectPtrString(Handle:hParams, num, offset, ObjectValueType:type, String:buffer[], size)
cell_t Native_GetParamObjectPtrString(IPluginContext *pContext, const cell_t *params)
{
HookParamsStruct *paramStruct;
if(!GetCallbackArgHandleIfValidOrError(g_HookParamsHandle, g_HookReturnHandle, (void **)&paramStruct, pContext, params[1]))
void *addr = NULL;
if (!GetObjectAddrOrThis(pContext, params, addr))
{
return 0;
}
if(params[2] <= 0 || params[2] > (int)paramStruct->dg->params.size())
{
return pContext->ThrowNativeError("Invalid param number %i max params is %i", params[2], paramStruct->dg->params.size());
}
int index = params[2] - 1;
if(paramStruct->dg->params.at(index).type != HookParamType_ObjectPtr && paramStruct->dg->params.at(index).type != HookParamType_Object)
{
return pContext->ThrowNativeError("Invalid object value type %i", paramStruct->dg->params.at(index).type);
}
size_t offset = GetParamOffset(paramStruct, index);
void *addr = GetObjectAddr(paramStruct->dg->params.at(index).type, paramStruct->dg->params.at(index).flags, paramStruct->orgParams, offset);
switch((ObjectValueType)params[4])
{
case ObjectValueType_CharPtr:
@ -1510,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

@ -162,6 +162,7 @@ public:
int entity;
ThisPointerType thisType;
HookType hookType;
CallingConvention thisFuncCallConv;
};
class DHooksCallback : public SourceHook::ISHDelegate, public DHooksInfo
@ -338,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

@ -1,6 +1,8 @@
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
#include "takedamageinfohack.h"
#include "smsdk_ext.h"
#include <ISDKHooks.h>
#include <convar.h>
@ -15,8 +17,6 @@
#include <itoolentity.h>
#endif
#include "takedamageinfohack.h"
#if SOURCE_ENGINE >= SE_CSS && SOURCE_ENGINE != SE_LEFT4DEAD
#define GETMAXHEALTH_IS_VIRTUAL
#endif

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

@ -4,26 +4,32 @@
{
"Offsets"
{
"CanBeAutobalanced"
{
"windows" "465"
"linux" "466"
}
"GroundEntChanged"
{
"windows" "188"
"linux" "190"
"windows" "178"
"linux" "180"
}
"OnTakeDamage_Alive"
{
"windows" "287"
"linux" "288"
"windows" "283"
"linux" "284"
}
"GetMaxHealth"
{
"windows" "126"
"linux" "127"
"windows" "118"
"linux" "119"
}
"Blocked"
{
"windows" "109"
"linux" "110"
"windows" "101"
"linux" "102"
}
/* CBaseCombatWeapon::Reload */
"Reload"
{
"windows" "287"
@ -31,18 +37,19 @@
}
"EndTouch"
{
"windows" "107"
"linux" "108"
"windows" "99"
"linux" "100"
}
/* CBaseEntity::FireBullets(FireBulletsInfo_t const&) */
"FireBullets"
{
"windows" "121"
"linux" "121"
"windows" "113"
"linux" "113"
}
"OnTakeDamage"
{
"windows" "69"
"linux" "70"
"windows" "61"
"linux" "62"
}
"PreThink"
{
@ -56,8 +63,8 @@
}
"SetTransmit"
{
"windows" "28"
"linux" "29"
"windows" "20"
"linux" "21"
}
"ShouldCollide"
{
@ -66,64 +73,64 @@
}
"Spawn"
{
"windows" "30"
"linux" "31"
"windows" "22"
"linux" "23"
}
"StartTouch"
{
"windows" "105"
"linux" "106"
"windows" "97"
"linux" "98"
}
"Think"
{
"windows" "57"
"linux" "58"
"windows" "49"
"linux" "50"
}
"Touch"
{
"windows" "106"
"linux" "107"
"windows" "98"
"linux" "99"
}
"TraceAttack"
{
"windows" "67"
"linux" "68"
"windows" "59"
"linux" "60"
}
"Use"
{
"windows" "104"
"linux" "105"
"windows" "96"
"linux" "97"
}
"VPhysicsUpdate"
{
"windows" "168"
"linux" "169"
"windows" "158"
"linux" "159"
}
"Weapon_CanSwitchTo"
{
"windows" "278"
"linux" "279"
"windows" "277"
"linux" "278"
}
"Weapon_CanUse"
{
"windows" "271"
"linux" "272"
}
"Weapon_Drop"
{
"windows" "274"
"linux" "275"
}
"Weapon_Equip"
{
"windows" "272"
"linux" "273"
}
"Weapon_Drop"
"Weapon_Switch"
{
"windows" "275"
"linux" "276"
}
"Weapon_Equip"
{
"windows" "273"
"linux" "274"
}
"Weapon_Switch"
{
"windows" "276"
"linux" "277"
}
}
}
}

View File

@ -23,13 +23,13 @@
}
"RemovePlayerItem"
{
"windows" "285"
"linux" "286"
"windows" "281"
"linux" "282"
}
"Weapon_GetSlot"
{
"windows" "280"
"linux" "281"
"windows" "279"
"linux" "280"
}
"Ignite"
{
@ -43,8 +43,8 @@
}
"Teleport"
{
"windows" "115"
"linux" "116"
"windows" "107"
"linux" "108"
}
"CommitSuicide"
{
@ -53,33 +53,33 @@
}
"GetVelocity"
{
"windows" "151"
"linux" "152"
"windows" "141"
"linux" "142"
}
"EyeAngles"
{
"windows" "142"
"linux" "143"
"windows" "132"
"linux" "133"
}
"AcceptInput"
{
"windows" "44"
"linux" "45"
"windows" "36"
"linux" "37"
}
"SetEntityModel"
{
"windows" "32"
"linux" "33"
"windows" "24"
"linux" "25"
}
"WeaponEquip"
{
"windows" "273"
"linux" "274"
"windows" "272"
"linux" "273"
}
"Activate"
{
"windows" "41"
"linux" "42"
"windows" "33"
"linux" "34"
}
"PlayerRunCmd"
{
@ -93,13 +93,13 @@
}
"SetOwnerEntity"
{
"windows" "25"
"linux" "26"
"windows" "17"
"linux" "18"
}
"GiveAmmo"
{
"windows" "264"
"linux" "265"
"windows" "263"
"linux" "264"
}
}
"Signatures"

@ -1 +1 @@
Subproject commit 38b4f3670826bbfe7c536ef33ec53f5971d419e2
Subproject commit c018a994478d6c31f70ded7adec7aa9e2c40bb1a

View File

@ -207,19 +207,28 @@ methodmap ArrayList < Handle {
// Returns the index for the first occurrence of the provided string. If
// the string cannot be located, -1 will be returned.
//
// @param item String to search for
// @param block Optionally which block to search in
// @param item String to search for.
// @param block Optionally which block to search in.
// @param start Index to start searching from (exclusive), or -1 for direction default.
// Valid numbers are in the interval [-1..Length].
// @param reverse Whether the search direction should be reversed.
// @param caseSensitive If true (default), comparison is case sensitive.
// If false, comparison is case insensitive.
// @return Array index, or -1 on failure
public native int FindString(const char[] item, int block=0);
// @error Invalid block, or invalid start index.
public native int FindString(const char[] item, int block=0, int start=-1, bool reverse=false, bool caseSensitive=true);
// Returns the index for the first occurrence of the provided value. If the
// value cannot be located, -1 will be returned.
//
// @param item Value to search for
// @param block Optionally which block to search in
// @return Array index, or -1 on failure
// @error Invalid block index
public native int FindValue(any item, int block=0);
// @param item Value to search for.
// @param block Optionally which block to search in.
// @param start Index to start searching from (exclusive), or -1 for direction default.
// Valid numbers are in the interval [-1..Length].
// @param reverse Whether the search direction should be reversed.
// @return Array index, or -1 on failure.
// @error Invalid block, or invalid start index.
public native int FindValue(any item, int block=0, int start=-1, bool reverse=false);
// Sort an ADT Array. Specify the type as Integer, Float, or String.
//

View File

@ -310,7 +310,7 @@ methodmap DHookParam < Handle
// Gets an object's variable value.
//
// @param num Parameter number to get, starting at 1.
// @param num Parameter number to get, 0 for param "this", other parameters start from 1
// @param offset Byte offset within the object to the var to get.
// @param type Type of var it is.
//
@ -320,7 +320,7 @@ methodmap DHookParam < Handle
// Gets an object's vector variable value.
//
// @param num Parameter number to get, starting at 1.
// @param num Parameter number to get, 0 for param "this", other parameters start from 1.
// @param offset Byte offset within the object to the var to get.
// @param type Type of var it is.
// @param vec Buffer to store the result vector.
@ -330,7 +330,7 @@ methodmap DHookParam < Handle
// Gets an object's string variable value.
//
// @param num Parameter number to get, starting at 1.
// @param num Parameter number to get, 0 for param "this", other parameters start from 1.
// @param offset Byte offset within the object to the var to get.
// @param type Type of var it is.
// @param buffer Buffer to store the result string.
@ -344,7 +344,7 @@ methodmap DHookParam < Handle
// The changes are only applied when MRES_ChangedHandled or MRES_ChangedOverride
// is returned in the callback.
//
// @param num Parameter number to set, starting at 1.
// @param num Parameter number to set, 0 for param "this", other parameters start from 1.
// @param offset Byte offset within the object to the var to set.
// @param type Type of var it is.
// @param value The value to set the var to.
@ -357,7 +357,7 @@ methodmap DHookParam < Handle
// The changes are only applied when MRES_ChangedHandled or MRES_ChangedOverride
// is returned in the callback.
//
// @param num Parameter number to set, starting at 1.
// @param num Parameter number to set, 0 for param "this", other parameters start from 1.
// @param offset Byte offset within the object to the var to set.
// @param type Type of var it is.
// @param vec The value to set the vector var to.
@ -928,7 +928,7 @@ native void DHookSetReturnString(Handle hReturn, char[] value);
* Gets an objects variable value
*
* @param hParams Handle to params structure
* @param num Param number to get.
* @param num Param number to get, 0 for param "this".
* @param offset Offset within the object to the var to get.
* @param type Type of var it is
*
@ -941,7 +941,7 @@ native any DHookGetParamObjectPtrVar(Handle hParams, int num, int offset, Object
* Sets an objects variable value
*
* @param hParams Handle to params structure
* @param num Param number to set.
* @param num Param number to set, 0 for param "this".
* @param offset Offset within the object to the var to set.
* @param type Type of var it is
* @param value The value to set the var to.
@ -954,7 +954,7 @@ native void DHookSetParamObjectPtrVar(Handle hParams, int num, int offset, Objec
* Gets an objects vector variable value
*
* @param hParams Handle to params structure
* @param num Param number to get.
* @param num Param number to get, 0 for param "this".
* @param offset Offset within the object to the var to get.
* @param type Type of var it is
* @param buffer Buffer to store the result vector
@ -967,7 +967,7 @@ native void DHookGetParamObjectPtrVarVector(Handle hParams, int num, int offset,
* Sets an objects vector variable value
*
* @param hParams Handle to params structure
* @param num Param number to set.
* @param num Param number to set, 0 for param "this".
* @param offset Offset within the object to the var to set.
* @param type Type of var it is
* @param value The value to set the vector var to.
@ -980,7 +980,7 @@ native void DHookSetParamObjectPtrVarVector(Handle hParams, int num, int offset,
* Gets an objects string variable value
*
* @param hParams Handle to params structure
* @param num Param number to get.
* @param num Param number to get, 0 for param "this".
* @param offset Offset within the object to the var to get.
* @param type Type of var it is
* @param buffer Buffer to store the result vector

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*)`) */

View File

@ -0,0 +1,141 @@
#pragma semicolon 1
#pragma newdecls required
#include <testing>
enum struct TestStruct
{
int intval;
char strval[32];
}
public void OnPluginStart()
{
ArrayList list = new ArrayList(sizeof(TestStruct));
// --------------------------------------------------------------------------------
SetTestContext("EmptyArrayTest");
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, -1, false), -1);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, -1, true), -1);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, -1, false), -1);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, -1, true), -1);
// --------------------------------------------------------------------------------
// Fill
TestStruct ts;
for (int i = 0; i < 10; i++)
{
ts.intval = i;
Format(ts.strval, sizeof(ts.strval), "index%d", i);
list.PushArray(ts);
}
// --------------------------------------------------------------------------------
SetTestContext("FindString");
AssertEq("test_defaults", list.FindString("index3", TestStruct::strval), 3);
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, -1, false), 3);
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, 0, false), 3);
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, 2, false), 3);
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, 3, false), -1);
AssertEq("test_forward", list.FindString("index3", TestStruct::strval, 10, false), -1);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, -1, true), 3);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, 0, true), -1);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, 3, true), -1);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, 4, true), 3);
AssertEq("test_reverse", list.FindString("index3", TestStruct::strval, 10, true), 3);
AssertEq("test_bottom", list.FindString("index0", TestStruct::strval, -1, false), 0);
AssertEq("test_bottom", list.FindString("index0", TestStruct::strval, -1, true), 0);
AssertEq("test_bottom", list.FindString("index0", TestStruct::strval, 1, true), 0);
AssertEq("test_bottom", list.FindString("index0", TestStruct::strval, 10, false), -1);
AssertEq("test_bottom", list.FindString("index0", TestStruct::strval, 10, true), 0);
AssertEq("test_top", list.FindString("index9", TestStruct::strval, -1, false), 9);
AssertEq("test_top", list.FindString("index9", TestStruct::strval, -1, true), 9);
AssertEq("test_top", list.FindString("index9", TestStruct::strval, 8, false), 9);
AssertEq("test_top", list.FindString("index9", TestStruct::strval, 10, false), -1);
AssertEq("test_top", list.FindString("index9", TestStruct::strval, 10, true), 9);
AssertEq("test_case_sensitive", list.FindString("INDEX0", TestStruct::strval, .caseSensitive = true), -1);
AssertEq("test_case_sensitive", list.FindString("INDEX0", TestStruct::strval, .caseSensitive = false), 0);
// --------------------------------------------------------------------------------
SetTestContext("FindValue");
AssertEq("test_defaults", list.FindValue(3, TestStruct::intval), 3);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, -1, false), 3);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, 0, false), 3);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, 2, false), 3);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, 3, false), -1);
AssertEq("test_forward", list.FindValue(3, TestStruct::intval, 10, false), -1);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, -1, true), 3);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, 0, true), -1);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, 3, true), -1);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, 4, true), 3);
AssertEq("test_reverse", list.FindValue(3, TestStruct::intval, 10, true), 3);
AssertEq("test_bottom", list.FindValue(0, TestStruct::intval, -1, false), 0);
AssertEq("test_bottom", list.FindValue(0, TestStruct::intval, -1, true), 0);
AssertEq("test_bottom", list.FindValue(0, TestStruct::intval, 1, true), 0);
AssertEq("test_bottom", list.FindValue(0, TestStruct::intval, 10, false), -1);
AssertEq("test_bottom", list.FindValue(0, TestStruct::intval, 10, true), 0);
AssertEq("test_top", list.FindValue(9, TestStruct::intval, -1, false), 9);
AssertEq("test_top", list.FindValue(9, TestStruct::intval, -1, true), 9);
AssertEq("test_top", list.FindValue(9, TestStruct::intval, 8, false), 9);
AssertEq("test_top", list.FindValue(9, TestStruct::intval, 10, false), -1);
AssertEq("test_top", list.FindValue(9, TestStruct::intval, 10, true), 9);
// --------------------------------------------------------------------------------
SetTestContext("IterateOverFindString");
int found, index;
// Duplicate last entry
list.PushArray(ts);
found = 0; index = -1;
while ((index = list.FindString("index9", TestStruct::strval, index, false)) != -1)
{
found++;
}
AssertEq("test_find_all_strings_forward", found, 2);
found = 0; index = -1;
while ((index = list.FindString("index9", TestStruct::strval, index, true)) != -1)
{
found++;
}
AssertEq("test_find_all_strings_reverse", found, 2);
// --------------------------------------------------------------------------------
SetTestContext("IterateOverFindValue");
found = 0, index = -1;
while ((index = list.FindValue(9, TestStruct::intval, index, false)) != -1)
{
found++;
}
AssertEq("test_find_all_values_forward", found, 2);
found = 0; index = -1;
while ((index = list.FindValue(9, TestStruct::intval, index, true)) != -1)
{
found++;
}
AssertEq("test_find_all_values_reverse", found, 2);
// --------------------------------------------------------------------------------
PrintToServer("OK");
}

@ -1 +1 @@
Subproject commit ab76f94ee4ef28187cc08030599577996fcba3be
Subproject commit 8c6692c85a6c41f5d89f744da57b5ba43515b4ec