diff --git a/amxmodx/Makefile b/amxmodx/Makefile
index f2ec112e..b92b1020 100755
--- a/amxmodx/Makefile
+++ b/amxmodx/Makefile
@@ -19,7 +19,7 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules
srvcmd.cpp strptime.cpp amxcore.cpp amxtime.cpp power.cpp amxxlog.cpp fakemeta.cpp \
amxxfile.cpp CLang.cpp md5.cpp emsg.cpp CForward.cpp CPlugin.cpp CModule.cpp \
CMenu.cpp util.cpp amx.cpp amxdbg.cpp natives.cpp newmenus.cpp debugger.cpp \
- optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp
+ optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp
LINK = /lib/libstdc++.a
diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h
index fc33bdf2..8b23592e 100755
--- a/amxmodx/amxmodx.h
+++ b/amxmodx/amxmodx.h
@@ -85,6 +85,7 @@ extern AMX_NATIVE_INFO string_Natives[];
extern AMX_NATIVE_INFO vault_Natives[];
extern AMX_NATIVE_INFO msg_Natives[];
extern AMX_NATIVE_INFO vector_Natives[];
+extern AMX_NATIVE_INFO g_SortNatives[];
#ifndef __linux__
#define DLLOAD(path) (DLHANDLE)LoadLibrary(path)
diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp
index 723da268..85748524 100755
--- a/amxmodx/modules.cpp
+++ b/amxmodx/modules.cpp
@@ -556,6 +556,7 @@ int set_amxnatives(AMX* amx, char error[128])
amx_Register(amx, g_DebugNatives, -1);
amx_Register(amx, msg_Natives, -1);
amx_Register(amx, vector_Natives, -1);
+ amx_Register(amx, g_SortNatives, -1);
//we're not actually gonna check these here anymore
amx->flags |= AMX_FLAG_PRENIT;
diff --git a/amxmodx/msvc/amxmodx_mm.vcproj b/amxmodx/msvc/amxmodx_mm.vcproj
index 5866b736..4a67603c 100755
--- a/amxmodx/msvc/amxmodx_mm.vcproj
+++ b/amxmodx/msvc/amxmodx_mm.vcproj
@@ -442,6 +442,9 @@
+
+
diff --git a/amxmodx/sorting.cpp b/amxmodx/sorting.cpp
new file mode 100644
index 00000000..3c74b99b
--- /dev/null
+++ b/amxmodx/sorting.cpp
@@ -0,0 +1,359 @@
+#include "amxmodx.h"
+#include
+
+/***********************************
+ * About the double array hack *
+ ***************************
+
+ Double arrays in Pawn are vectors offset but the current offset. For example:
+
+ new array[2][2]
+
+ In this array, index 0 contains the offset from the current offset which
+ results in the final vector [2] (at [0][2]). Meaning, to dereference [1][2],
+ it is equivalent to:
+
+ address = &array[1] + array[1] + 2 * sizeof(cell)
+
+ The fact that each offset is from the _current_ position rather than the _base_
+ position is very important. It means that if you to try to swap vector positions,
+ the offsets will no longer match, because their current position has changed. A
+ simple and ingenious way around this is to back up the positions in a separate array,
+ then to overwrite each position in the old array with absolute indices. Pseudo C++ code:
+
+ cell *array; //assumed to be set to the 2+D array
+ cell *old_offsets = new cell[2];
+ for (int i=0; i<2; i++)
+ {
+ old_offsets = array[i];
+ array[i] = i;
+ }
+
+ Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses.
+ After sorting/modification is done, you must relocate the new indices. For example, if the two vectors in our
+ demo array were swapped, array[0] would be 1 and array[1] would be 0. This is invalid to the virtual machine.
+ Luckily, this is also simple -- all the information is there.
+
+ for (int i=0; i<2; i++)
+ {
+ //get the # of the vector we want to relocate in
+ cell vector_index = array[i];
+ //get the real address of this vector
+ char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index];
+ //calc and store the new distance offset
+ array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) )
+ }
+
+ Note that the inner expression can be heavily reduced; it is expanded for readability.
+ **********************************/
+
+enum SortOrder
+{
+ Sort_Ascending = 0,
+ Sort_Descending = 1,
+};
+
+int sort_ints_asc(const void *int1, const void *int2)
+{
+ return (*(int *)int1) - (*(int *)int2);
+}
+
+int sort_ints_desc(const void *int1, const void *int2)
+{
+ return (*(int *)int2) - (*(int *)int1);
+}
+
+static cell AMX_NATIVE_CALL SortIntegers(AMX *amx, cell *params)
+{
+ cell *array = get_amxaddr(amx, params[1]);
+ cell array_size = params[2];
+ cell type = params[3];
+
+ if (type == Sort_Ascending)
+ {
+ qsort(array, array_size, sizeof(cell), sort_ints_asc);
+ } else {
+ qsort(array, array_size, sizeof(cell), sort_ints_desc);
+ }
+
+ return 1;
+}
+
+int sort_floats_asc(const void *float1, const void *float2)
+{
+ REAL r1 = *(REAL *)float1;
+ REAL r2 = *(REAL *)float2;
+
+ if (r1 < r2)
+ {
+ return -1;
+ } else if (r2 < r1) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int sort_floats_desc(const void *float1, const void *float2)
+{
+ REAL r1 = *(REAL *)float1;
+ REAL r2 = *(REAL *)float2;
+
+ if (r1 < r2)
+ {
+ return 1;
+ } else if (r2 < r1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static cell AMX_NATIVE_CALL SortFloats(AMX *amx, cell *params)
+{
+ cell *array = get_amxaddr(amx, params[1]);
+ cell array_size = params[2];
+ cell type = params[3];
+
+ if (type == Sort_Ascending)
+ {
+ qsort(array, array_size, sizeof(cell), sort_floats_asc);
+ } else {
+ qsort(array, array_size, sizeof(cell), sort_floats_desc);
+ }
+
+ return 1;
+}
+
+static cell *g_CurStringArray = NULL;
+static cell *g_CurRebaseMap = NULL;
+
+int sort_strings_asc(const void *blk1, const void *blk2)
+{
+ cell reloc1 = *(cell *)blk1;
+ cell reloc2 = *(cell *)blk2;
+
+ register cell *str1 = (cell *)((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
+ register cell *str2 = (cell *)((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
+
+ while (*str1 == *str2++)
+ {
+ if (*str1++ == 0)
+ {
+ return 0;
+ }
+ }
+
+ return (*str1 - *(str2 - 1));
+}
+
+int sort_strings_desc(const void *blk1, const void *blk2)
+{
+ cell reloc1 = *(cell *)blk1;
+ cell reloc2 = *(cell *)blk2;
+
+ register cell *str1 = (cell *)((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
+ register cell *str2 = (cell *)((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
+
+ while (*str1 == *str2++)
+ {
+ if (*str1++ == 0)
+ {
+ return 0;
+ }
+ }
+
+ return (*(str2 - 1) - *str1);
+}
+
+static cell AMX_NATIVE_CALL SortStrings(AMX *amx, cell *params)
+{
+ cell *array = get_amxaddr(amx, params[1]);
+ cell array_size = params[2];
+ cell type = params[3];
+
+ /** HACKHACK - back up the old indices, replace the indices with something easier */
+ cell amx_addr, *phys_addr;
+ int err;
+ if ((err=amx_Allot(amx, array_size, &amx_addr, &phys_addr)) != AMX_ERR_NONE)
+ {
+ LogError(amx, err, "Ran out of memory");
+ return 0;
+ }
+
+ g_CurStringArray = array;
+ g_CurRebaseMap = phys_addr;
+
+ for (int i=0; i g_AMXSortStack;
+
+int sort1d_amx_custom(const void *elem1, const void *elem2)
+{
+ cell c1 = *(cell *)elem1;
+ cell c2 = *(cell *)elem2;
+ sort_info *pInfo = g_AMXSortStack.front();
+
+ return executeForwards(pInfo->pfn, c1, c2, pInfo->array_addr, pInfo->data_addr, pInfo->data_size);
+}
+
+static cell AMX_NATIVE_CALL SortCustom1D(AMX *amx, cell *params)
+{
+ cell *array = get_amxaddr(amx, params[1]);
+ cell array_size = params[2];
+ int len;
+ const char *funcname = get_amxstring(amx, params[3], 0, len);
+
+ int pfn = registerSPForwardByName(amx, funcname, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL);
+ if (pfn < 0)
+ {
+ LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcname);
+ return 0;
+ }
+
+ sort_info *pInfo = new sort_info;
+
+ pInfo->pfn = pfn;
+ pInfo->data_addr = params[4];
+ pInfo->data_size = params[5];
+ pInfo->array_addr = params[1];
+ pInfo->array_remap = NULL;
+ pInfo->array_base = NULL;
+
+ g_AMXSortStack.push(pInfo);
+ qsort(array, array_size, sizeof(cell), sort1d_amx_custom);
+ g_AMXSortStack.pop();
+
+ delete pInfo;
+
+ return 1;
+}
+
+int sort2d_amx_custom(const void *elem1, const void *elem2)
+{
+ cell c1 = *(cell *)elem1;
+ cell c2 = *(cell *)elem2;
+ sort_info *pInfo = g_AMXSortStack.front();
+
+ cell c1_addr = pInfo->array_addr + (c1 * sizeof(cell)) + pInfo->array_remap[c1];
+ cell c2_addr = pInfo->array_addr + (c2 * sizeof(cell)) + pInfo->array_remap[c2];
+
+ cell *c1_r = get_amxaddr(pInfo->amx, c1_addr);
+ cell *c2_r = get_amxaddr(pInfo->amx, c2_addr);
+
+ return executeForwards(pInfo->pfn, c1_addr, c2_addr, pInfo->array_addr, pInfo->data_addr, pInfo->data_size);
+}
+
+static cell AMX_NATIVE_CALL SortCustom2D(AMX *amx, cell *params)
+{
+ cell *array = get_amxaddr(amx, params[1]);
+ cell array_size = params[2];
+ int len;
+ const char *funcname = get_amxstring(amx, params[3], 0, len);
+
+ /** back up the old indices, replace the indices with something easier */
+ cell amx_addr, *phys_addr;
+ int err;
+ if ((err=amx_Allot(amx, array_size, &amx_addr, &phys_addr)) != AMX_ERR_NONE)
+ {
+ LogError(amx, err, "Ran out of memory");
+ return 0;
+ }
+
+ int pfn = registerSPForwardByName(amx, funcname, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL);
+ if (pfn < 0)
+ {
+ amx_Release(amx, amx_addr);
+ LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", funcname);
+ return 0;
+ }
+
+ sort_info *pInfo = new sort_info;
+
+ pInfo->pfn = pfn;
+ pInfo->data_addr = params[4];
+ pInfo->data_size = params[5];
+ pInfo->array_addr = params[1];
+ pInfo->amx = amx;
+
+ /** Same process as in strings, back up the old indices for later fixup */
+ pInfo->array_base = array;
+ pInfo->array_remap = phys_addr;
+
+ for (int i=0; ipfn);
+ delete pInfo;
+
+ return 1;
+}
+
+AMX_NATIVE_INFO g_SortNatives[] =
+{
+ {"SortIntegers", SortIntegers},
+ {"SortFloats", SortFloats},
+ {"SortStrings", SortStrings},
+ {"SortCustom1D", SortCustom1D},
+ {"SortCustom2D", SortCustom2D},
+
+ {NULL, NULL},
+};
diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc
index 940af59d..c4367f2a 100755
--- a/plugins/include/amxmodx.inc
+++ b/plugins/include/amxmodx.inc
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
/* Function is called just after server activation.
* Good place for configuration loading, commands and cvars registration. */
diff --git a/plugins/include/sorting.inc b/plugins/include/sorting.inc
new file mode 100644
index 00000000..dc3907b2
--- /dev/null
+++ b/plugins/include/sorting.inc
@@ -0,0 +1,76 @@
+/* Sorting functions.
+ *
+ * by the AMX Mod X Development Team
+ *
+ * This file is provided as is (no warranties).
+ *
+ * All sort functions are based off the qsort() function from the
+ * C standard library, which uses the Quick Sort algorithm.
+ * For more info, see: http://linux.wku.edu/~lamonml/algor/sort/sort.html
+ */
+
+#if defined _time_included
+ #endinput
+#endif
+#define _time_included
+
+enum SortMethod
+{
+ Sort_Ascending = 0,
+ Sort_Descending = 1,
+};
+
+/**
+ * Basic sorting functions below.
+ */
+
+native SortIntegers(array[], array_size, SortMethod:order = Sort_Ascending);
+
+native SortFloats(Float:array[], array_size, SortMethod:order = Sort_Ascending);
+
+native SortStrings(array[][], num_strings, SortMethod:order = Sort_Ascending);
+
+/**
+ * Custom sorting functions below.
+ */
+
+/**
+ * Sorts a custom 1D array. You must pass in a comparison function.
+ * The sorting algorithm then uses your comparison function to sort the data.
+ * The function is called in the following manner:
+ *
+ * public MySortFunc(elem1, elem2, const array[], const data[], data_size)
+ *
+ * elem1, elem2 - Current element pair being compared
+ * array[] - Array in its current mid-sorted state.
+ * data[] - Extra data array you passed to the sort func.
+ * data_size - Size of extra data you passed to the sort func.
+ *
+ * Your function should return:
+ * -1 if elem1 should go before elem2
+ * 0 if elem1 and elem2 are equal
+ * 1 if elem1 should go after elem2
+ * Note that the parameters after elem2 are all optional and you do not need to specify them.
+ */
+native SortCustom1D(array[], array_size, const comparefunc[], data[]="", data_size=0);
+
+
+/**
+ * Sorts a custom 2D array.
+ * The sorting algorithm then uses your comparison function to sort the data.
+ * The function is called in the following manner:
+ *
+ * public MySortFunc(const elem1[], const elem2[], const array[], data[], data_size)
+ *
+ * elem1[], elem2[] - Current element array pairs being compared
+ * array[][] - Array in its currently being sorted state.
+ * data[] - Extra data array you passed to the sort func.
+ * data_size - Size of extra data you passed to the sort func.
+ *
+ * Your function should return:
+ * -1 if elem1[] should go before elem2[]
+ * 0 if elem1[] and elem2 are equal[]
+ * 1 if elem1[] should go after elem2[]
+ * Note that the parameters after elem2[] are all optional and you do not need to specify them.
+ */
+native SortCustom2D(array[][], array_size, const comparefunc[], data[]="", data_size=0);
diff --git a/plugins/testsuite/sorttest.sma b/plugins/testsuite/sorttest.sma
new file mode 100644
index 00000000..2b998fb4
--- /dev/null
+++ b/plugins/testsuite/sorttest.sma
@@ -0,0 +1,153 @@
+#include
+
+public plugin_init()
+{
+ register_plugin("Sort Test", "1.0", "BAILOPAN")
+
+ register_srvcmd("test_sort_ints", "Command_TestSortInts")
+ register_srvcmd("test_sort_floats", "Command_TestSortFloats")
+ register_srvcmd("test_sort_strings", "Command_TestSortStrings")
+ register_srvcmd("test_sort_1d", "Command_TestSort1D")
+ register_srvcmd("test_sort_2d", "Command_TestSort2D")
+}
+
+/*****************
+ * INTEGER TESTS *
+ *****************/
+// Note that integer comparison is just int1-int2 (or a variation therein)
+
+PrintIntegers(const array[], size)
+{
+ for (new i=0; i elem2)
+ {
+ return -1;
+ } else if (elem1 < elem2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+public Command_TestSort1D()
+{
+ new Float:array[10] = {6.3, 7.6, 3.2, 2.1, 8.5, 5.2, 0.4, 1.7, 4.8, 8.2}
+
+ SortCustom1D(_:array, 10, "Custom1DSort")
+ PrintFloats(array, 10)
+
+ return PLUGIN_HANDLED
+}
+
+/***************************
+ * String comparison tests *
+ ***************************/
+
+PrintStrings(const array[][], size)
+{
+ for (new i=0; i