/*
Legal:
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 the "License"; you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the YSI framework.
The Initial Developer of the Original Code is Alex "Y_Less" Cole.
Portions created by the Initial Developer are Copyright C 2011
the Initial Developer. All Rights Reserved.
Contributors:
Y_Less
koolk
JoeBullet/Google63
g_aSlice/Slice
Misiur
samphunter
tianmeta
maddinat0r
spacemud
Crayder
Dayvison
Ahmad45123
Zeex
irinel1996
Yiin-
Chaprnks
Konstantinos
Masterchen09
Southclaws
PatchwerkQWER
m0k1
paulommu
udan111
Thanks:
JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
ZeeX - Very productive conversations.
koolk - IsPlayerinAreaEx code.
TheAlpha - Danish translation.
breadfish - German translation.
Fireburn - Dutch translation.
yom - French translation.
50p - Polish translation.
Zamaroht - Spanish translation.
Los - Portuguese translation.
Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
me to strive to better.
Pixels^ - Running XScripters where the idea was born.
Matite - Pestering me to release it and using it.
Very special thanks to:
Thiadmer - PAWN, whose limits continue to amaze me!
Kye/Kalcor - SA:MP.
SA:MP Team past, present and future - SA:MP.
Optional plugins:
Gamer_Z - GPS.
Incognito - Streamer.
Me - sscanf2, fixes2, Whirlpool.
*/
// Helper macro so people don't need to enter all the sizes all the time.
#define _Jagged(%0) (%0),sizeof(%0),sizeof(%0[])
#define _Jagged_Sum:(%0,%1) _Jagged_Sum:(%0+%1)
// Ceiling division so that all slots will be at least big enough. Otherwise
//
// Jagged:X[3]<12, 1 1>
//
// Would result in "[3][4]", which doesn't have 14 slots total.
#define Jagged:%0[%1]<%2>; %0[%1][_:_Jagged_Sum:(%2,%1,-1) / (%1)];Jagged_ResizeAll(%0,%2);
static stock Jagged_MovePtr(array[][], maxSize, slot, shift)
{
new
//tmp0,
//tmp1,
ptr;
// Get the slot pointer.
#emit LOAD.S.alt array
#emit LOAD.S.pri slot
#emit IDXADDR
//#emit STOR.S.pri tmp0
//#emit MOVE.alt
#emit LOAD.I
#emit STOR.S.pri ptr
//printf("Jagged_MovePtr: Moving %d by %d to %d (mod %d)", ptr, shift, ptr + shift, maxSize);
ptr += shift;
// I have to do it this way to avoid a bug with "if" statements and "#emit".
ptr = (ptr > maxSize) ? maxSize : ptr;
#emit LOAD.S.alt array
#emit LOAD.S.pri slot
#emit IDXADDR
//#emit STOR.S.pri tmp1
#emit STOR.S.pri shift
#emit LOAD.S.pri ptr
#emit SREF.S.pri shift
//printf("%d %d %d %d", tmp0, tmp1, shift, ptr);
#if _DEBUG >= 7
printf("Jagged_MovePtr: Header:");
_Jagged_PrintHeader(array, 4, 0);
#endif
}
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* The slot to resize.
* The new size of the slot.
*
* Resize an array slot, maintining all of its data. The "slot" variable is
* usually used to hold the NEXT slot - we barely need the number of the
* current slot once we have its address.
*
*//*------------------------------------------------------------------------**/
stock bool:_Jagged_ResizeOne(array[][], size1, size2, slot, newSize)
{
if (newSize < 0)
{
return false;
}
// SLOT GETS INCREMENTED VERY FAST!
if (0 < ++slot < size1)
{
// Get the slot's current size and address.
newSize *= 4;
new
newEnd = _Jagged_Address(array, slot - 1) + newSize,
arrayEnd = _Jagged_End(array, size1, size2);
if (newEnd > arrayEnd)
{
P:1("Insufficient space to grow jagged array.");
return false;
}
new
slotEnd = _Jagged_Address(array, slot),
//oldSize = slotEnd - slotStart,
shift = newEnd - slotEnd; //newSize - oldSize;
P:5("Jagged_ResizeOne: Resizing slot %d from %d to %d", slot - 1, _Jagged_SizeOf(array, size1, size2, slot - 1), newSize);
if (shift != 0)
{
new
remain = arrayEnd - newEnd;
// Grow the slot. This has to be done largely in assembly to remove
// the "BOUNDS" OpCodes that would otherwise be inserted. Actually,
// we can't have "BOUNDS" here because there's no way for the
// compiler to know the array's size in advance. This works even
// when "shift" is negative.
memcpy(array[slot][shift / 4], array[slot], 0, remain, remain / 4);
// Now shift all the subsequent slots.
size2 = size2 * size1 * 4;
while (slot < size1)
{
Jagged_MovePtr(array, size2 + (size1 - slot) * 4, slot, shift);
++slot;
}
if (shift > 0)
{
// Blank the remainder of the slot we are growing.
rawMemset(slotEnd, 0, shift);
}
else
{
// Blank the end of the array.
rawMemset(arrayEnd + shift, 0, -shift);
}
}
// Do nothing if they're the same.
return true;
}
P:C(else if (slot == size1) printf("Cannot alter the last slot in an array."););
return false;
}
#define Jagged_ResizeOne(%0,%1) _Jagged_ResizeOne(_Jagged(%0),%1)
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* Multiple {slot, size} tuples.
*
* Resize multiple array slots, maintining all of their data.
*
*//*------------------------------------------------------------------------**/
stock bool:_Jagged_Resize(array[][], size1, size2, ...)
{
new
num = numargs();
// Now, as you can imagine, this requires some tricky low-level code, but
// that is all in other functions now.
for (new i = 3; i != num; ++i)
{
P:7("_Jagged_Resize: loop %d %d %d", i, getarg(i, 0), getarg(i, 1));
if (!_Jagged_ResizeOne(array, size1, size2, getarg(i, 0), getarg(i, 1)))
return false;
}
return true;
}
#define Jagged_Resize(%0,%1) _Jagged_Resize(_Jagged(%0),%1)
#define _ALS_Jagged_Resize
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* New sizes for every slot.
*
* Resize multilpe array slots, maintining all of their data.
*
*//*------------------------------------------------------------------------**/
stock bool:_Jagged_ResizeAll(array[][], size1, size2, ...)
{
new
num = min(numargs() - 3, size1);
// Now, as you can imagine, this requires some tricky low-level code, but
// that is all in other functions now.
for (new i = 3, j = 0; j != num; ++i, ++j)
{
P:7("_Jagged_Resize: loop %d %d %d", i, j, getarg(i));
_Jagged_ResizeOne(array, size1, size2, j, getarg(i));
}
}
#define Jagged_ResizeAll(%0,%1) _Jagged_ResizeAll(_Jagged(%0),%1)
#define _ALS_Jagged_ResizeAll
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* The slot to resize.
* The new size of the slot.
*
* The "slot" variable is usually used to hold the NEXT slot - we barely need
* the number of the current slot once we have its address. "Resize" copies
* data to keep as much of the original data as possible. This just moves the
* boundaries.
*
*//*------------------------------------------------------------------------**/
stock bool:Jagged_MoveOne(array[][], size1, size2, slot, newSize)
{
if (newSize < 0)
{
return false;
}
// SLOT GETS INCREMENTED VERY FAST!
if (0 < ++slot < size1)
{
// Get the slot's current size and address.
newSize *= 4;
new
newEnd = _Jagged_Address(array, slot - 1) + newSize,
arrayEnd = _Jagged_End(array, size1, size2);
if (newEnd > arrayEnd)
{
P:1("Insufficient space to grow jagged array.");
return false;
}
new
slotEnd = _Jagged_Address(array, slot),
shift = newEnd - slotEnd;
P:5("Jagged_MoveOne: Mov slot %d from %d to %d", slot - 1, _Jagged_SizeOf(array, size1, size2, slot - 1), newSize);
if (shift != 0)
{
// Shift all the subsequent slots.
size2 = size2 * size1 * 4;
while (slot < size1)
{
Jagged_MovePtr(array, size2 + (size1 - slot) * 4, slot, shift);
++slot;
}
}
// Do nothing if they're the same.
return true;
}
P:C(else if (slot == size1) printf("Cannot alter the last slot in an array."););
return false;
}
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* Multiple {slot, size} tuples.
*
* "Resize" copies data to keep as much of the original data as possible. This
* just moves the boundaries.
*
*//*------------------------------------------------------------------------**/
stock bool:_Jagged_Move(array[][], size1, size2, ...)
{
new
num = numargs();
// Now, as you can imagine, this requires some tricky low-level code, but
// that is all in other functions now.
for (new i = 3; i != num; ++i)
{
P:7("_Jagged_Move: loop %d %d %d", i, getarg(i, 0), getarg(i, 1));
Jagged_MoveOne(array, size1, size2, getarg(i, 0), getarg(i, 1));
}
}
#define Jagged_Move(%0,%1) _Jagged_Move(_Jagged(%0),%1)
#define _ALS_Jagged_Move
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
*
* Changes all pointers except the first one to point at the very end of the
* array. I.e. makes sizeof (array[0]) == sizeof (array) * sizeof
* (array[])
* and sizeof (array[n > 0]) == 0.
*
*//*------------------------------------------------------------------------**/
stock _Jagged_Empty(array[][], size1, size2)
{
// The size of the main storage area, including the header.
size2 = (size1 + size1 * size2) * cellbytes;
while (--size1)
{
#emit LOAD.S.pri array
#emit ADD.C 4
#emit MOVE.alt
#emit LOAD.S.pri size2
#emit ADD.C 0xFFFFFFFC // -4
#emit STOR.I
#emit STOR.S.pri size2
#emit STOR.S.alt array
}
}
#define Jagged_Empty(%0) _Jagged_Empty(_Jagged(%0))
#define _ALS_Jagged_Empty
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The slot we want to resize.
* The new size of the slot.
*
* This purely moves the next pointer in the array. It does nothing at all
* with any data stored in the array, or any other pointers. This means many
* bad things can happen when it is used incorrectly. Just one example is
* that extending a slot over the next few slots will mean those later slots
* will now point in to the middle of the current slot's data. This is best
* used in conjunction with Jagged_Empty, so that the array is an almost blank
* slate before dangerous messing.
*
*//*------------------------------------------------------------------------**/
stock Jagged_UnsafeResizeOne(array[][], slot, newSize)
{
// Get a pointer to the next slot.
#emit LOAD.S.alt array
#emit LOAD.S.pri slot
#emit INC.I
#emit IDXADDR
#emit PUSH.pri
// Get the offset to this array's current start point.
#emit LOAD.S.pri slot
#emit LIDX
// Get the offset to the new end.
#emit LOAD.S.alt newSize
#emit XCHG
#emit IDXADDR
// Subtract 4 to make it the offset for the next slot, save it to the
// pointer we pushed earlier.
#emit ADD.C 0xFFFFFFFC
#emit SREF.S.pri 0xFFFFFFFC
// End.
#emit POP.pri
#emit RETN
__COMPILER_NAKED
}
//#define Jagged_UnsafeResizeOne(%0,%1) _Jagged_UnsafeResizeOne(_Jagged(%0),%1)
//#define _ALS_Jagged_UnsafeResizeOne
/*-------------------------------------------------------------------------*//**
* The array we want to resize a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* New sizes for every slot.
*
* "Resize" copies data to keep as much of the original data as possible. This
* just moves the boundaries.
*
*//*------------------------------------------------------------------------**/
stock bool:_Jagged_MoveAll(array[][], size1, size2, ...)
{
new
num = min(numargs() - 3, size1);
// Now, as you can imagine, this requires some tricky low-level code, but
// that is all in other functions now.
for (new i = 3, j = 0; j != num; ++i, ++j)
{
P:7("_Jagged_Move: loop %d %d %d", i, j, getarg(i));
Jagged_MoveOne(array, size1, size2, j, getarg(i));
}
}
#define Jagged_MoveAll(%0,%1) _Jagged_MoveAll(_Jagged(%0),%1)
#define _ALS_Jagged_MoveAll
/*-------------------------------------------------------------------------*//**
* The array we want to get the size of a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* The slot to get the size of.
*
* The current size of this slot. This is NOT a compile-time operation.
*
*
* Returns the data in BYTES NOT CELLS.
*
*//*------------------------------------------------------------------------**/
stock _Jagged_SizeOf(array[][], size1, size2, slot)
{
if (0 <= slot < size1)
{
new
start = _Jagged_Address(array, slot);
if (++slot == size1)
{
// Figure out the end address of the array.
return _Jagged_End(array, size1, size2) - start;
}
else
{
return _Jagged_Address(array, slot) - start;
}
}
return -1;
}
#define Jagged_SizeOf(%0,%1) _Jagged_SizeOf(_Jagged(%0),%1)
#define _ALS_Jagged_SizeOf
#define jaggedsizeof(%0[%1]) (_Jagged_SizeOf(_Jagged(%0),%1)>>2)
/*-------------------------------------------------------------------------*//**
* The array we want to get the address of a slot in.
* The number of slots in the array.
* The ORIGINAL size of every slot.
* The slot to get the address of.
*
* The absolute address of this slot.
*
*//*------------------------------------------------------------------------**/
#if _DEBUG == 0
static
#endif
stock _Jagged_Address(array[][], slot)
{
// Get the slot pointer.
#emit LOAD.S.alt array
#emit LOAD.S.pri slot
#emit IDXADDR
#emit MOVE.alt
#emit LOAD.I
#emit ADD
#emit RETN
__COMPILER_NAKED
}
/*-------------------------------------------------------------------------*//**
* The array we want to get the end of.
* The number of slots in the array.
* The ORIGINAL size of every slot.
*
* The absolute address of the end of this array.
*
*//*------------------------------------------------------------------------**/
#if _DEBUG == 0
static
#endif
stock _Jagged_End(array[][], size1, size2)
{
#emit LOAD.S.alt size2
#emit INC.alt
#emit LOAD.S.pri size1
#emit UMUL
#emit LOAD.S.alt array
#emit IDXADDR
#emit RETN
__COMPILER_NAKED
}
stock _Jagged_PrintHeader(array[][], size1, size2)
{
#pragma unused size2
for (new ptr, slot = 0; slot != size1; ++slot)
{
// Get the slot pointer.
#emit LOAD.S.alt array
#emit LOAD.S.pri slot
#emit LIDX
#emit PUSH.pri
// Get the relative offset.
#emit LOAD.S.pri slot
#emit LOAD.S.alt size1
#emit SUB.alt
#emit SHL.C.pri 2
// Adjust the output to absolute.
#emit POP.alt
#emit SUB.alt
// #emit LOAD.S.alt array
// #emit ADD
#emit STOR.S.pri ptr
printf("Array header: %d = %d", slot, ptr);
}
}
#define Jagged_PrintHeader(%0) _Jagged_PrintHeader(_Jagged(%0))
#define _ALS_Jagged_PrintHeader