Skip to content

Commit 841a08b

Browse files
author
Alex Cole
committed
Make public variables more accessible. They are a core part of pawn, but are hard to use, so extremely rarely used.
`getpubvar`, `setpubvar`, `existpubvar`, `numpubvars`.
1 parent fe18c11 commit 841a08b

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

core.inc

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,212 @@ native deleteproperty(id = 0, const name[] = "", value = cellmin);
212212
*/
213213
native bool:existproperty(id = 0, const name[] = "", value = cellmin);
214214

215+
/* */ native __packpubvar(dest[], const source[], maxlength = sizeof (dest)) = strpack;
216+
217+
static stock __findpubvar(const __name[])
218+
{
219+
// Scans the AMX header to find a public variable and returns the address of the entry, or `0`
220+
// if there isn't one. Can't use `static` here because we rely on the string being all `\0`s.
221+
new __packed[64 char], __offset = 0;
222+
const __cellbytes = cellbits / charbits;
223+
// In 32-bit scripts this is `64`, but in 64-bit scripts it is only `96`. Part of the header
224+
// entry is always 32-bit, regardless of the cell bit width. We need it in bytes.
225+
const __defsize = __cellbytes + 4;
226+
// Convert the normal input string to a C string, via a packed string. Pack it, then reverse
227+
// every individual cell to get the correct final memory layout.
228+
__packpubvar(__packed, __name);
229+
// All cells have trailing nulls, so this can only be true when all bytes are non-zero because
230+
// the top byte is non-zero.
231+
while ((__packed[__offset] = swapchars(__packed[__offset])) > (cellmax >>> (charbits - 1)))
232+
{
233+
++__offset;
234+
}
235+
new __start, __end;
236+
// Now we start doing assembly magic to dig through the AMX header itself.
237+
// Get the offset from the start of the file to the start of the data segment (DAT).
238+
#emit LCTRL 1
239+
// Invert it to get the start of the file relative to DAT.
240+
#emit NEG
241+
// Store this offset. We'll need it a lot.
242+
#emit MOVE.alt
243+
#emit STOR.S.alt __offset
244+
// Get the offset to the pubvars pointer.
245+
#emit ADD.C 44 // Doesn't change with `cellbits`.
246+
#emit STOR.S.pri __start
247+
#emit LREF.S.pri __start
248+
// Make this pointer relative to `DAT`.
249+
#emit ADD
250+
// Point to the name pointer in the entry to begin with.
251+
#emit ADD.C __cellbytes
252+
#emit STOR.S.pri __start
253+
// Get the offset to the tags pointer.
254+
#emit MOVE.pri
255+
#emit ADD.C 48 // Doesn't change with `cellbits`.
256+
#emit STOR.S.pri __end
257+
#emit LREF.S.pri __end
258+
#emit ADD
259+
#emit ADD.C __cellbytes
260+
#emit STOR.S.pri __end
261+
// Loop through the
262+
new __s0, __s1, __s2;
263+
// This could be made a binary search, but it would complicate the code a lot, and isn't worth
264+
// it given how rare public variables are. In fact, amx_assembly doesn't even bother with
265+
// comparing four bytes at once and just loads and compares single characters instead, which is
266+
// an option... It would simplify the code even more.
267+
while (__start != __end)
268+
{
269+
// Load the pointer.
270+
#emit LREF.S.pri __start
271+
// Add the `DAT` offset.
272+
#emit LOAD.S.alt __offset
273+
#emit ADD
274+
// Save the pointer.
275+
#emit STOR.S.pri __s0
276+
__s1 = 0;
277+
for ( ; ; )
278+
{
279+
#emit LREF.S.pri __s0
280+
#emit STOR.S.pri __s2
281+
if (__packed[__s1] == __s2)
282+
{
283+
__s0 += __cellbytes;
284+
++__s1;
285+
continue;
286+
}
287+
// They are different, but this could be because of junk data loaded from after the end
288+
// of the pubvar name in the AMX. We need to determine whether the `NULL` comes first.
289+
__s1 = __packed[__s1];
290+
while (__s1 && (__s1 & 0xFF) == (__s2 & 0xFF))
291+
{
292+
__s1 >>>= 8;
293+
__s2 >>>= 8;
294+
}
295+
if ((__s1 & 0xFF) || (__s2 & 0xFF))
296+
{
297+
break;
298+
}
299+
// Enough matches.
300+
return __start - __cellbytes;
301+
}
302+
// This wasn't a match. Try the next one.
303+
__start += __defsize;
304+
}
305+
return 0;
306+
}
307+
308+
/**
309+
* <library>core</library>
310+
* <summary>Gets a specific public variable from the current script.</summary>
311+
* <param name="name">The public variable's name.</param>
312+
* <seealso name="setpubvar"/>
313+
* <seealso name="existpubvar"/>
314+
* <seealso name="numpubvars"/>
315+
* <returns>Naught if the variable doesn't exist (use <c>existpubvar</c> first).</returns>
316+
*/
317+
/*
318+
native getpubvar(const name[]);
319+
*/
320+
stock getpubvar(const __name[])
321+
{
322+
new __entry = __findpubvar(__name);
323+
if (__entry)
324+
{
325+
// Get the pointer to the data.
326+
#emit LREF.S.pri __entry
327+
#emit STOR.S.pri __entry
328+
// Get the old data (fortunately the pointer is relative to `DAT`).
329+
#emit LREF.S.pri __entry
330+
// Return the value in `pri`.
331+
#emit STACK 4
332+
#emit RETN
333+
}
334+
return 0;
335+
}
336+
337+
/**
338+
* <library>core</library>
339+
* <summary>Sets a specific public variable in the current script.</summary>
340+
* <param name="name">The public variable's name.</param>
341+
* <param name="value">The value to store in the public variable.</param>
342+
* <seealso name="getpubvar"/>
343+
* <seealso name="existpubvar"/>
344+
* <seealso name="numpubvars"/>
345+
* <returns>The previous value of the variable.</returns>
346+
*/
347+
/*
348+
native setpubvar(const name[], value);
349+
*/
350+
stock setpubvar(const __name[], __value)
351+
{
352+
new __entry = __findpubvar(__name);
353+
if (__entry)
354+
{
355+
// Get the pointer to the data.
356+
#emit LREF.S.pri __entry
357+
#emit STOR.S.pri __entry
358+
// Get the old data (fortunately the pointer is relative to `DAT`).
359+
#emit LREF.S.pri __entry
360+
// Store the new data.
361+
#emit LOAD.S.alt __value
362+
#emit SREF.S.alt __entry
363+
// Return the value in `pri`.
364+
#emit STACK 4
365+
#emit RETN
366+
}
367+
return 0;
368+
}
369+
370+
/**
371+
* <library>core</library>
372+
* <summary>Checks if a specific public variable exists in the current script.</summary>
373+
* <param name="name">The public variable's name.</param>
374+
* <seealso name="getpubvar"/>
375+
* <seealso name="setpubvar"/>
376+
* <seealso name="numpubvars"/>
377+
* <returns>Does the variable exist?</returns>
378+
*/
379+
/*
380+
native bool:existpubvar(const name[]);
381+
*/
382+
stock bool:existpubvar(const __name[])
383+
{
384+
return __findpubvar(__name) != 0;
385+
}
386+
387+
/**
388+
* <library>core</library>
389+
* <summary>Counts how many public variables there are in the script.</summary>
390+
* <seealso name="getpubvar"/>
391+
* <seealso name="setpubvar"/>
392+
* <seealso name="existpubvar"/>
393+
* <returns>The number of public variables declared.</returns>
394+
*/
395+
/*
396+
native numpubvars();
397+
*/
398+
stock numpubvars()
399+
{
400+
// In 32-bit scripts this is `64`, but in 64-bit scripts it is only `96`. Part of the header
401+
// entry is always 32-bit, regardless of the cell bit width. We need it in bytes.
402+
const __defsize = cellbits / charbits + 4;
403+
new __start, __end;
404+
// Now we start doing assembly magic to dig through the AMX header itself.
405+
// Get the offset from the start of the file to the start of the data segment (DAT).
406+
#emit LCTRL 1
407+
// Invert it to get the start of the file relative to DAT.
408+
#emit NEG
409+
// Get the offset to the pubvars pointer.
410+
#emit ADD.C 44 // Doesn't change with `cellbits`.
411+
#emit STOR.S.pri __start
412+
#emit LREF.S.pri __start
413+
#emit STOR.S.pri __start
414+
// Get the offset to the tags pointer.
415+
#emit LCTRL 1
416+
#emit NEG
417+
#emit ADD.C 48 // Doesn't change with `cellbits`.
418+
#emit STOR.S.pri __end
419+
#emit LREF.S.pri __end
420+
#emit STOR.S.pri __end
421+
return (__end - __start) / __defsize;
422+
}
423+

0 commit comments

Comments
 (0)