amxmodx/amxmodx/datastructs.cpp
2014-08-04 13:18:19 -05:00

924 lines
20 KiB
C++

// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
#include "amxmodx.h"
#include "datastructs.h"
#include <am-utility.h>
// Note: All handles start at 1. 0 and below are invalid handles.
// This way, a plugin that doesn't initialize a vector or
// string will not be able to modify another plugin's data
// on accident.
ke::Vector<CellArray*> VectorHolder;
// Array:ArrayCreate(cellsize=1, reserved=32);
static cell AMX_NATIVE_CALL ArrayCreate(AMX* amx, cell* params)
{
// params[1] (cellsize) is how big in cells each element is.
// this MUST be greater than 0!
int cellsize = params[1];
// params[2] (reserved) is how many elements to allocate
// immediately when the list is created.
int reserved = params[2];
if (cellsize <= 0)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid array size (%d)", cellsize);
return -1;
}
// Scan through the vector list to see if any are NULL.
// NULL means the vector was previously destroyed.
for (unsigned int i=0; i < VectorHolder.length(); ++i)
{
if (VectorHolder[i]==NULL)
{
VectorHolder[i] = new CellArray(cellsize);
if (reserved > 0)
{
VectorHolder[i]->resize(reserved);
}
return i + 1;
}
}
// None are NULL, create a new vector
CellArray* NewVector = new CellArray(cellsize);
if (reserved > 0)
{
NewVector->resize(reserved);
}
VectorHolder.append(NewVector);
return VectorHolder.length();
}
// native ArrayClear(Array:which);
static cell AMX_NATIVE_CALL ArrayClear(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
vec->clear();
return 1;
}
// native ArraySize(Array:which);
static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
return vec->size();
}
// native bool:ArrayResize(Array:which, newsize);
static cell AMX_NATIVE_CALL ArrayResize(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
if (!vec->resize(params[2]))
{
LogError(amx, AMX_ERR_NATIVE, "Unable to resize array to \"%u\"", params[2]);
return 0;
}
return 1;
}
// native Array:ArrayClone(Array:which);
static cell AMX_NATIVE_CALL ArrayClone(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
CellArray *clonevec = vec->clone();
// Scan through the vector list to see if any are NULL.
// NULL means the vector was previously destroyed.
for (unsigned int i = 0; i < VectorHolder.length(); ++i)
{
if (VectorHolder[i] == NULL)
{
VectorHolder[i] = clonevec;
return i + 1;
}
}
VectorHolder.append(clonevec);
return VectorHolder.length();
}
// native ArrayGetArray(Array:which, item, any:output[], size = -1);
static cell AMX_NATIVE_CALL ArrayGetArray(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
size_t indexes = vec->blocksize();
if (params[4] != -1 && (size_t)params[4] <= vec->blocksize())
{
indexes = params[4];
}
cell *addr = get_amxaddr(amx, params[3]);
memcpy(addr, blk, sizeof(cell) * indexes);
return indexes;
}
// native any:ArrayGetCell(Array:which, item, block = 0, bool:asChar = false);
static cell AMX_NATIVE_CALL ArrayGetCell(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
idx = (size_t)params[3];
if (!params[4])
{
if (idx >= vec->blocksize())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid block %d (blocksize: %d)", idx, vec->blocksize());
return 0;
}
return blk[idx];
}
else
{
if (idx >= vec->blocksize() * 4)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid byte %d (blocksize: %d bytes)", idx, vec->blocksize() * 4);
return 0;
}
return (cell)*((char *)blk + idx);
}
return 0;
}
// native ArrayGetString(Array:which, item, output[], size);
static cell AMX_NATIVE_CALL ArrayGetString(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
return set_amxstring_utf8(amx, params[3], blk, amxstring_len(blk), params[4] + 1); // + EOS.
}
// native ArraySetArray(Array:which, item, const any:input[], size =-1);
static cell AMX_NATIVE_CALL ArraySetArray(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
size_t indexes = vec->blocksize();
if (params[4] != -1 && (size_t)params[4] <= vec->blocksize())
{
indexes = params[4];
}
cell *addr = get_amxaddr(amx, params[3]);
memcpy(blk, addr, sizeof(cell) * indexes);
return indexes;
}
// native ArraySetCell(Array:which, item, any:input, block = 0, bool:asChar = false);
static cell AMX_NATIVE_CALL ArraySetCell(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
idx = (size_t)params[4];
if (params[5] == 0)
{
if (idx >= vec->blocksize())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid block %d (blocksize: %d)", idx, vec->blocksize());
return 0;
}
blk[idx] = params[3];
}
else
{
if (idx >= vec->blocksize() * 4)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid byte %d (blocksize: %d bytes)", idx, vec->blocksize() * 4);
return 0;
}
*((char *)blk + idx) = (char)params[3];
}
return 1;
}
// native ArraySetString(Array:which, item, const input[]);
static cell AMX_NATIVE_CALL ArraySetString(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell *blk = vec->at(idx);
int len;
char *str = get_amxstring(amx, params[3], 0, len);
return strncopy(blk, str, ke::Min((size_t)len + 1, vec->blocksize()));
}
// native ArrayPushArray(Array:which, const any:input[], size = -1);
static cell AMX_NATIVE_CALL ArrayPushArray(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
cell *blk = vec->push();
if (!blk)
{
LogError(amx, AMX_ERR_NATIVE, "Failed to grow array");
return 0;
}
cell *addr = get_amxaddr(amx, params[2]);
size_t indexes = vec->blocksize();
if (params[3] != -1 && (size_t)params[3] <= vec->blocksize())
{
indexes = params[3];
}
memcpy(blk, addr, sizeof(cell) * indexes);
return static_cast<cell>((vec->size() - 1));
}
// native ArrayPushCell(Array:which, any:input);
static cell AMX_NATIVE_CALL ArrayPushCell(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
cell *blk = vec->push();
if (!blk)
{
LogError(amx, AMX_ERR_NATIVE, "Failed to grow array");
return 0;
}
*blk = params[2];
return static_cast<cell>((vec->size() - 1));
}
// native ArrayPushString(Array:which, const input[]);
static cell AMX_NATIVE_CALL ArrayPushString(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
cell *blk = vec->push();
if (!blk)
{
LogError(amx, AMX_ERR_NATIVE, "Failed to grow array");
return 0;
}
strncopy(blk, get_amxaddr(amx, params[2]), vec->blocksize());
return static_cast<cell>((vec->size() - 1));
}
// native DoNotUse : ArrayGetStringHandle(Array : which, item);
static cell AMX_NATIVE_CALL ArrayGetStringHandle(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
cell* ptr = vec->at(idx);
if (ptr == NULL)
{
return 0;
}
return reinterpret_cast<cell>(ptr);
}
// native ArrayInsertArrayAfter(Array:which, item, const any:input[]);
static cell AMX_NATIVE_CALL ArrayInsertArrayAfter(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2] + 1;
if (idx > vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayAfter (%d:%d)", idx, vec->size());
return 0;
}
cell *addr = get_amxaddr(amx, params[3]);
memcpy(vec->insert_at(idx), addr, sizeof(cell) * vec->blocksize());
return 1;
}
// native ArrayInsertCellAfter(Array:which, item, any:input);
static cell AMX_NATIVE_CALL ArrayInsertCellAfter(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2] + 1;
if (idx > vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellAfter (%d:%d)", idx, vec->size());
return 0;
}
*vec->insert_at(idx) = params[3];
return 1;
}
// native ArrayInsertStringAfter(Array:which, item, const input[]);
static cell AMX_NATIVE_CALL ArrayInsertStringAfter(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2] + 1;
if (idx > vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringAfter (%d:%d)", idx, vec->size());
return 0;
}
int len;
const char *str = get_amxstring(amx, params[3], 0, len);
return strncopy(vec->insert_at(idx), str, ke::Min((size_t)len + 1, vec->blocksize()));
}
// native ArrayInsertArrayBefore(Array:which, item, const any:input[]);
static cell AMX_NATIVE_CALL ArrayInsertArrayBefore(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", idx, vec->size());
return 0;
}
cell *addr = get_amxaddr(amx, params[3]);
memcpy(vec->insert_at(idx), addr, vec->blocksize() * sizeof(cell));
return 1;
}
// native ArrayInsertCellBefore(Array:which, item, const any:input);
static cell AMX_NATIVE_CALL ArrayInsertCellBefore(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", idx, vec->size());
return 0;
}
*vec->insert_at(idx) = params[3];
return 1;
}
// native ArrayInsertStringBefore(Array:which, item, const input[]);
static cell AMX_NATIVE_CALL ArrayInsertStringBefore(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", idx, vec->size());
return 0;
}
int len;
const char *str = get_amxstring(amx, params[3], 0, len);
return strncopy(vec->insert_at(idx), str, ke::Min((size_t)len + 1, vec->blocksize()));
}
// native ArraySwap(Array:which, item1, item2);
static cell AMX_NATIVE_CALL ArraySwap(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx1 = (size_t)params[2];
size_t idx2 = (size_t)params[3];
if (idx1 >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx1, vec->size());
return 0;
}
if (idx2 >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx2, vec->size());
return 0;
}
vec->swap(idx1, idx2);
return 1;
}
// native ArrayDeleteItem(Array:which, item);
static cell AMX_NATIVE_CALL ArrayDeleteItem(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return 0;
}
size_t idx = (size_t)params[2];
if (idx >= vec->size())
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d (count: %d)", idx, vec->size());
return 0;
}
vec->remove(idx);
return 1;
}
// native ArrayDestroy(&Array:which);
static cell AMX_NATIVE_CALL ArrayDestroy(AMX* amx, cell* params)
{
cell* handle = get_amxaddr(amx, params[1]);
CellArray* vec = HandleToVector(amx, *handle);
if (vec == NULL)
{
return 0;
}
delete vec;
VectorHolder[*handle-1] = NULL;
*handle = 0;
return 1;
}
struct ArraySort_s
{
int func;
cell array_hndl;
cell* array_base;
cell array_bsize;
cell data;
cell size;
cell addr1;
cell addr2;
AMX *amx;
};
ArraySort_s SortInfo;
int SortArrayList(const void *elem1, const void *elem2)
{
return executeForwards(
SortInfo.func,
SortInfo.array_hndl,
((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize,
((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize,
SortInfo.data,
SortInfo.size
);
}
// native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0);
static cell AMX_NATIVE_CALL ArraySort(AMX* amx, cell* params)
{
int handle = params[1];
CellArray* vec = HandleToVector(amx, handle);
if (vec == NULL)
{
return 0;
}
int len;
char* funcName = get_amxstring(amx, params[2], 0, len);
// MySortFunc(Array:array, item1, item2, const data[], data_size)
int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
if (func < 0)
{
LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcName);
return 0;
}
size_t arraysize = vec->size();
size_t blocksize = vec->blocksize();
cell *array = vec->base();
ArraySort_s oldinfo = SortInfo;
SortInfo.func = func;
SortInfo.array_base = array;
SortInfo.array_bsize = static_cast<cell>(blocksize);
SortInfo.array_hndl = handle;
SortInfo.data = params[3];
SortInfo.size = params[4];
qsort(array, arraysize, blocksize * sizeof(cell), SortArrayList);
SortInfo = oldinfo;
unregisterSPForward(func);
return 1;
}
int SortArrayListExCell(const void *elem1, const void *elem2)
{
size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize;
size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize;
return executeForwards(
SortInfo.func,
SortInfo.array_hndl,
*&SortInfo.array_base[index1 * SortInfo.array_bsize],
*&SortInfo.array_base[index2 * SortInfo.array_bsize],
SortInfo.data,
SortInfo.size
);
}
int SortArrayListExArray(const void *elem1, const void *elem2)
{
size_t index1 = ((cell)((cell *)elem1 - SortInfo.array_base)) / SortInfo.array_bsize;
size_t index2 = ((cell)((cell *)elem2 - SortInfo.array_base)) / SortInfo.array_bsize;
cell *addr1 = get_amxaddr(SortInfo.amx, SortInfo.addr1);
cell *addr2 = get_amxaddr(SortInfo.amx, SortInfo.addr2);
memcpy(addr1, &SortInfo.array_base[index1 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell));
memcpy(addr2, &SortInfo.array_base[index2 * SortInfo.array_bsize], SortInfo.array_bsize * sizeof(cell));
return executeForwards(
SortInfo.func,
SortInfo.array_hndl,
SortInfo.addr1,
SortInfo.addr2,
SortInfo.data,
SortInfo.size
);
}
// native ArraySortEx(Array:array, const comparefunc[], data[]="", data_size=0);
static cell AMX_NATIVE_CALL ArraySortEx(AMX* amx, cell* params)
{
int handle = params[1];
CellArray* vec = HandleToVector(amx, handle);
if (vec == NULL)
{
return 0;
}
int len;
char* funcName = get_amxstring(amx, params[2], 0, len);
int func = registerSPForwardByName(amx, funcName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE);
if (!func)
{
LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcName);
return 0;
}
size_t arraysize = vec->size();
size_t blocksize = vec->blocksize();
cell *array = vec->base();
cell amx_addr1 = 0, amx_addr2 = 0, *phys_addr = NULL;
if (blocksize > 1)
{
int err;
if ((err = amx_Allot(amx, blocksize, &amx_addr1, &phys_addr)) != AMX_ERR_NONE
|| ( err = amx_Allot(amx, blocksize, &amx_addr2, &phys_addr)) != AMX_ERR_NONE)
{
LogError(amx, err, "Ran out of memory");
return 0;
}
}
ArraySort_s oldinfo = SortInfo;
SortInfo.func = func;
SortInfo.array_base = array;
SortInfo.array_bsize = static_cast<cell>(blocksize);
SortInfo.array_hndl = params[1];
SortInfo.data = params[3];
SortInfo.size = params[4];
SortInfo.amx = amx;
SortInfo.addr1 = amx_addr1;
SortInfo.addr2 = amx_addr2;
qsort(array, arraysize, blocksize * sizeof(cell), blocksize > 1 ? SortArrayListExArray : SortArrayListExCell);
SortInfo = oldinfo;
if (blocksize > 1)
{
amx_Release(amx, amx_addr1);
amx_Release(amx, amx_addr2);
}
unregisterSPForward(func);
return 1;
}
extern bool fastcellcmp(cell *a, cell *b, cell len);
extern int amxstring_len(cell* a);
// native ArrayFindString(Array:which, const item[]);
static cell AMX_NATIVE_CALL ArrayFindString(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return -1;
}
cell *b, *a = get_amxaddr(amx, params[2]);
size_t cellcount = vec->blocksize();
size_t a_len = ke::Max(1, amxstring_len(a));
size_t len = a_len > cellcount ? cellcount : a_len;
for (size_t i = 0; i < vec->size(); i++)
{
b = vec->at(i);
if (fastcellcmp(a, b, len))
{
return static_cast<cell>(i);
}
}
return -1;
}
// native ArrayFindValue(Array:which, any:item);
static cell AMX_NATIVE_CALL ArrayFindValue(AMX* amx, cell* params)
{
CellArray* vec = HandleToVector(amx, params[1]);
if (vec == NULL)
{
return -1;
}
for (size_t i = 0; i < vec->size(); i++)
{
if (params[2] == *vec->at(i))
{
return static_cast<cell>(i);
}
}
return -1;
}
AMX_NATIVE_INFO g_DataStructNatives[] =
{
{ "ArrayCreate", ArrayCreate },
{ "ArrayClear", ArrayClear },
{ "ArrayClone", ArrayClone },
{ "ArraySize", ArraySize },
{ "ArrayResize", ArrayResize },
{ "ArrayGetArray", ArrayGetArray },
{ "ArrayGetCell", ArrayGetCell },
{ "ArrayGetString", ArrayGetString },
{ "ArraySetArray", ArraySetArray },
{ "ArraySetCell", ArraySetCell },
{ "ArraySetString", ArraySetString },
{ "ArrayPushArray", ArrayPushArray },
{ "ArrayPushCell", ArrayPushCell },
{ "ArrayPushString", ArrayPushString },
{ "ArrayInsertArrayAfter", ArrayInsertArrayAfter },
{ "ArrayInsertCellAfter", ArrayInsertCellAfter },
{ "ArrayInsertStringAfter", ArrayInsertStringAfter },
{ "ArrayInsertArrayBefore", ArrayInsertArrayBefore },
{ "ArrayInsertCellBefore", ArrayInsertCellBefore },
{ "ArrayInsertStringBefore", ArrayInsertStringBefore },
{ "ArraySwap", ArraySwap },
{ "ArrayDeleteItem", ArrayDeleteItem },
{ "ArrayGetStringHandle", ArrayGetStringHandle },
{ "ArrayDestroy", ArrayDestroy },
{ "ArraySort", ArraySort },
{ "ArraySortEx", ArraySortEx },
{ "ArrayFindString", ArrayFindString },
{ "ArrayFindValue", ArrayFindValue },
{ NULL, NULL }
};