Update: 2.0 released. Check changelog below.
How? Dark Magic!
Used a few tricks i created functions for variable arguments access.
This workable only with non-array variables for now (write if it needed to add arrays/strings support).
Instruction:
1) Include valist.inc in plugin with #include directive (
You must include it first among all includes for proper work).
2) Call va_start(int param_n) in variable argument function, where param_n number of argument with ..., counting of arguments start with 1.
Note for 1st version: It's important call va_start before any variable will declared. Overwise, it'll work not correct.
(In 1st version need to call va_start first in function, in 2nd not).
3) For getting next argument use va_arg(). In case if no more arguments to get, it'll throw error.
You can use va_args() for getting count of params passed in function and va_more() to check if all arguments getted.
4) When you no more needed to get arguments, you must call va_end(). Overwise, memory leaks.
Offtop: Please, if you see any grammar mistakes, notice me about it. I'm not going well in English.
Plugin example:
PHP Code:
// © Maxim "Kailo" Telezhenko, 2017
#pragma newdecls required
#pragma semicolon 1
#include <valist>
public void OnPluginStart()
{
RegConsoleCmd("sm_nums", Cmd_Nums);
}
public Action Cmd_Nums(int client, int args)
{
ReplyToCommand(client, "Sum: %i", Sum(10, 40, 50, -30, 80));
return Plugin_Handled;
}
int Sum(int ...)
{
va_start(1);
PrintToServer("args: %i", va_args());
int sum = 0;
while (va_more())
sum += va_arg();
va_end();
return sum;
}
valist.inc code
PHP Code:
// © Maxim "Kailo" Telezhenko, 2017
// Version 2
// Struct of valist
// int arg_count; // 0
// int location; // 1
// int iterator; // 2
// total: // 3
enum
{
m_arg_count = 0,
m_location = 1,
m_iterator = 2,
};
// Global anchor for memory access
stock static int g_anchor[1] = 1337; // =)
// Allow to get variable local address in plugin memory
stock static int GetAddr(any[] array)
{
int anchor[1];
return GetAddrWorker(anchor, array);
}
// "any ..." needed for escape warning "symbol is never used" in previous function
stock static int GetAddrWorker(int[] anchor, any ...)
{
return anchor[4];
}
stock static void GetOldFrameBase(int[] anchor)
{
anchor[0] = anchor[2];
}
// Allow to get value form plugin memory (DAT, heap, stack) with local address
stock static int Deref(int local_addr)
{
return DerefWorker(g_anchor, local_addr);
}
// Will throw error if anchor_addr bigger than local_addr
// Therefore: valist must be included first for lower anchor_addr
stock static int DerefWorker(int[] anchor, int local_addr)
{
static int anchor_addr = -1;
if (anchor_addr == -1)
{
anchor_addr = GetAddr(g_anchor);
}
return anchor[(local_addr - anchor_addr) / 4];
}
#define THIS (view_as<ArrayList>(this))
#define member_set(%0,%1,%2) %0.Set(0, %2, %1, false)
#define member_get(%0,%1) %0.Get(0, %1, false)
methodmap Valist
{
property int arg_count
{
public get() { return member_get(THIS,m_arg_count); }
}
public Valist(int param_n)
{
int anchor[1];
GetOldFrameBase(anchor); // result stored in anchor[0]
ArrayList base = new ArrayList(3, 1);
member_set(base,m_arg_count,Deref(anchor[0] + 8));
member_set(base,m_location,anchor[0] + 12);
member_set(base,m_iterator,param_n);
return view_as<Valist>(base);
}
public void Close()
{
THIS.Close();
}
public any Arg()
{
int iterator = member_get(THIS,m_iterator);
if (iterator > member_get(THIS,m_arg_count))
ThrowError("End of parameters reached.");
member_set(THIS,m_iterator,iterator + 1);
return Deref(Deref(member_get(THIS,m_location) + (iterator - 1) * 4));
}
public bool More()
{
return member_get(THIS,m_iterator) <= member_get(THIS,m_arg_count);
}
}
#undef THIS
#define va_start(%0) Valist VALIST = Valist(%0)
// if have more args to read
#define va_more() VALIST.More()
#define va_arg() VALIST.Arg()
#define va_args() VALIST.arg_count
// Must called when list no more needed
#define va_end() VALIST.Close()
Attention!
1st version not working
in 1.9 since
sourcepawn "Disallow negative indices in all array access patterns" (
d7743c3) update.
Changelog:
v2:
Changelog
v1: First release.
// Download latest version below in attachments.
Old versions:
v1:
valist.inc
How i did this: (tricks)
1) Unchecked memory access
Then you pass array as param, plugin know only address and check index for negative value
PHP Code:
{
int array[1];
int var1 = 50;
PrintToServer("%i %i", Fn(array), var1);
}
int Fn(int[] array)
{
return array[1]; // will return var1 value
}
2) Knowledge of SourcePawn virtual machine
Plugin memory сonsists of DAT (globals, statics, consts), heap, stack (order as i wrote).
When functions calls:
1) Pass arguments in reverse order
2) Pass num of arguments
3) pass old frame address
4) pass 0
Using sipmle trick i can dump stack
PHP Code:
void Fn()
{
int a[1] = {777};
dumpstack(a);
}
stock void dumpstack(int[] anchor)
{
for (int a = 0; a <= 15; a++)
PrintToServer("%i: %i", a, anchor[a]);
}
Using this in process of writing include i got ():
Code:
etc.
-8: 2220 // unused memory
-7: 20780 // unused memory
-6: 20776 // unused memory
-5: -5 // value of a
-4: 0 // just 0
-3: 20804 // old frame // for example look at {1}
-2: 1 // param count
-1: 20800 // 1st param; address of a // Here is started vm_start() call
0: 666 // a[1] = {666} // address of this array is 20800
/// {1} Here beging frame of vm_start(), here address is 20804
1: 0 // just 0
2: 20824 // old frame
3: 1 // param count
4: 1 // 1st param // Here is started vm_start() call
5: 0 // Valist VALIST variable
6: 0 // just 0
7: 20856 // old frame
8: 5 // arg count in Sum call
9: 2264 // 1st param
10: 2260 // 2nd param
11: 2256 // 3rd param
12: 2252 // 4th param
13: 2248 // 5th param // Here is started Sum() call; All 5 params is heap address
14: 2236 // this heap addr of where will stored result of Sum() for call ReplyToCommand in future
15: 20876 // this addr of "Sum: %i" for call ReplyToCommand in future
All addresses is local for plugin memory.
Also as we see stack reversed in memory.
memory: DAT-start ... DAT-end, heap-start ... heap-end, stack-end ... stack-start;
3) pseudo-class based on methodmap + ArrayList
4) Some macros for good looking code