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.
// 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]; }
// 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()
// 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
v2 changelog:
1) Now working with 1.9:
Code with negative indexes changed to trick: include declare global variable in start of DAT -> variable has low address -> get variable address -> use trick 1 with offset with difference between addresses
2) Changed way to get address and number params -> va_start no more needed to be called first in function.
3) All variables and functions marked as stocks for not including then not used.