Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.
(This day shall henceforth be known as the Eighty Column Massacre of '07)
3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include "sm_globals.h"
33 #include <IHandleSys.h>
37 /***********************************
38 * About the double array hack *
39 ***************************
41 Double arrays in Pawn are vectors offset by the current offset. For example:
45 In this array, index 0 contains the offset from the current offset which
46 results in the final vector [2] (at [0][2]). Meaning, to dereference [1][2],
49 address = &array[1] + array[1] + 2 * sizeof(cell)
51 The fact that each offset is from the _current_ position rather than the _base_
52 position is very important. It means that if you to try to swap vector positions,
53 the offsets will no longer match, because their current position has changed. A
54 simple and ingenious way around this is to back up the positions in a separate array,
55 then to overwrite each position in the old array with absolute indices. Pseudo C++ code:
57 cell *array; //assumed to be set to the 2+D array
58 cell *old_offsets = new cell[2];
59 for (int i=0; i<2; i++)
61 old_offsets = array[i];
65 Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses.
66 After sorting/modification is done, you must relocate the new indices. For example, if the two vectors in our
67 demo array were swapped, array[0] would be 1 and array[1] would be 0. This is invalid to the virtual machine.
68 Luckily, this is also simple -- all the information is there.
70 for (int i=0; i<2; i++)
72 //get the # of the vector we want to relocate in
73 cell vector_index = array[i];
74 //get the real address of this vector
75 char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index];
76 //calc and store the new distance offset
77 array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) )
80 Note that the inner expression can be heavily reduced; it is expanded for readability.
81 **********************************/
89 int sort_ints_asc(const void *int1, const void *int2)
91 return (*(int *)int1) - (*(int *)int2);
94 int sort_ints_desc(const void *int1, const void *int2)
96 return (*(int *)int2) - (*(int *)int1);
99 static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
102 cell_t array_size = params[2];
103 cell_t type = params[3];
105 pContext->LocalToPhysAddr(params[1], &array);
107 if (type == Sort_Ascending)
109 qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
111 qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
117 int sort_floats_asc(const void *float1, const void *float2)
119 float r1 = *(float *)float1;
120 float r2 = *(float *)float2;
125 } else if (r2 < r1) {
132 int sort_floats_desc(const void *float1, const void *float2)
134 float r1 = *(float *)float1;
135 float r2 = *(float *)float2;
140 } else if (r2 < r1) {
147 static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
150 cell_t array_size = params[2];
151 cell_t type = params[3];
153 pContext->LocalToPhysAddr(params[1], &array);
155 if (type == Sort_Ascending)
157 qsort(array, array_size, sizeof(cell_t), sort_floats_asc);
159 qsort(array, array_size, sizeof(cell_t), sort_floats_desc);
165 static cell_t *g_CurStringArray = NULL;
166 static cell_t *g_CurRebaseMap = NULL;
168 int sort_strings_asc(const void *blk1, const void *blk2)
170 cell_t reloc1 = *(cell_t *)blk1;
171 cell_t reloc2 = *(cell_t *)blk2;
173 char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
174 char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
176 return strcmp(str1, str2);
179 int sort_strings_desc(const void *blk1, const void *blk2)
181 cell_t reloc1 = *(cell_t *)blk1;
182 cell_t reloc2 = *(cell_t *)blk2;
184 char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
185 char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
187 return strcmp(str2, str1);
190 static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
193 cell_t array_size = params[2];
194 cell_t type = params[3];
196 pContext->LocalToPhysAddr(params[1], &array);
198 /** HACKHACK - back up the old indices, replace the indices with something easier */
199 cell_t amx_addr, *phys_addr;
201 if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
203 pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
206 g_CurStringArray = array;
207 g_CurRebaseMap = phys_addr;
209 for (int i=0; i<array_size; i++)
211 phys_addr[i] = array[i];
215 if (type == Sort_Ascending)
217 qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
219 qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
222 /* END HACKHACK - restore what we damaged so Pawn doesn't throw up.
223 * We'll browse through each index of the array and patch up the distance.
225 for (int i=0; i<array_size; i++)
227 /* Compute the final address of the old array and subtract the new location.
228 * This is the fixed up distance.
230 array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
233 pContext->HeapPop(amx_addr);
235 g_CurStringArray = NULL;
236 g_CurRebaseMap = NULL;
243 IPluginFunction *pFunc;
250 sort_info g_SortInfo;
252 int sort1d_amx_custom(const void *elem1, const void *elem2)
254 cell_t c1 = *(cell_t *)elem1;
255 cell_t c2 = *(cell_t *)elem2;
258 IPluginFunction *pf = g_SortInfo.pFunc;
261 pf->PushCell(g_SortInfo.array_addr);
262 pf->PushCell(g_SortInfo.hndl);
263 pf->Execute(&result);
268 static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
271 cell_t array_size = params[2];
273 IPluginFunction *pFunction = pContext->GetFunctionById(params[3]);
276 return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
279 pContext->LocalToPhysAddr(params[1], &array);
281 sort_info oldinfo = g_SortInfo;
284 g_SortInfo.hndl = params[4];
285 g_SortInfo.array_addr = params[1];
286 g_SortInfo.array_remap = NULL;
287 g_SortInfo.array_base = NULL;
288 g_SortInfo.pFunc = pFunction;
290 qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
292 g_SortInfo = oldinfo;
297 int sort2d_amx_custom(const void *elem1, const void *elem2)
299 cell_t c1 = *(cell_t *)elem1;
300 cell_t c2 = *(cell_t *)elem2;
302 cell_t c1_addr = g_SortInfo.array_addr + (c1 * sizeof(cell_t)) + g_SortInfo.array_remap[c1];
303 cell_t c2_addr = g_SortInfo.array_addr + (c2 * sizeof(cell_t)) + g_SortInfo.array_remap[c2];
305 IPluginContext *pContext = g_SortInfo.pFunc->GetParentContext();
307 pContext->LocalToPhysAddr(c1_addr, &c1_r);
308 pContext->LocalToPhysAddr(c2_addr, &c2_r);
311 g_SortInfo.pFunc->PushCell(c1_addr);
312 g_SortInfo.pFunc->PushCell(c2_addr);
313 g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
314 g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
315 g_SortInfo.pFunc->Execute(&result);
320 static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
323 cell_t array_size = params[2];
324 IPluginFunction *pFunction;
326 pContext->LocalToPhysAddr(params[1], &array);
328 if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
330 return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
333 /** back up the old indices, replace the indices with something easier */
334 cell_t amx_addr, *phys_addr;
336 if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
338 pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
342 sort_info oldinfo = g_SortInfo;
344 g_SortInfo.pFunc = pFunction;
345 g_SortInfo.hndl = params[4];
346 g_SortInfo.array_addr = params[1];
348 /** Same process as in strings, back up the old indices for later fixup */
349 g_SortInfo.array_base = array;
350 g_SortInfo.array_remap = phys_addr;
352 for (int i=0; i<array_size; i++)
354 phys_addr[i] = array[i];
358 qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
360 /** Fixup process! */
361 for (int i=0; i<array_size; i++)
363 /* Compute the final address of the old array and subtract the new location.
364 * This is the fixed up distance.
366 array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
369 pContext->HeapPop(amx_addr);
371 g_SortInfo = oldinfo;
376 REGISTER_NATIVES(sortNatives)
378 {"SortIntegers", sm_SortIntegers},
379 {"SortFloats", sm_SortFloats},
380 {"SortStrings", sm_SortStrings},
381 {"SortCustom1D", sm_SortCustom1D},
382 {"SortCustom2D", sm_SortCustom2D},