core/smn_sorting.cpp
author Michael McKoy <ferret99gt@gmail.com>
Wed Aug 08 21:32:06 2007 +0000 (2007-08-08)
changeset 1276 501613e0859f
parent 1231 17badaf66ce4
child 1330 ab94316bb52b
permissions -rw-r--r--
Added sm_cancelvote to the base commands, as well as appropriate translations.
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include "sm_globals.h"
    34 #include <IHandleSys.h>
    35 #include <stdlib.h>
    36 #include <string.h>
    37 
    38 /***********************************
    39  *   About the double array hack   *
    40      ***************************   
    41 
    42  Double arrays in Pawn are vectors offset by the current offset.  For example:
    43 
    44  new array[2][2]
    45 
    46    In this array, index 0 contains the offset from the current offset which
    47  results in the final vector [2] (at [0][2]).  Meaning, to dereference [1][2], 
    48  it is equivalent to:
    49 
    50  address = &array[1] + array[1] + 2 * sizeof(cell)
    51 
    52    The fact that each offset is from the _current_ position rather than the _base_
    53  position is very important.  It means that if you to try to swap vector positions,
    54  the offsets will no longer match, because their current position has changed.  A 
    55  simple and ingenious way around this is to back up the positions in a separate array,
    56  then to overwrite each position in the old array with absolute indices.  Pseudo C++ code:
    57 
    58  cell *array; //assumed to be set to the 2+D array
    59  cell *old_offsets = new cell[2];
    60  for (int i=0; i<2; i++)
    61  {
    62     old_offsets = array[i];
    63 	array[i] = i;
    64  }
    65 
    66    Now, you can swap the array indices with no problem, and do a reverse-lookup to find the original addresses.
    67  After sorting/modification is done, you must relocate the new indices.  For example, if the two vectors in our
    68  demo array were swapped, array[0] would be 1 and array[1] would be 0.  This is invalid to the virtual machine.
    69  Luckily, this is also simple -- all the information is there.
    70 
    71  for (int i=0; i<2; i++)
    72  {
    73 	//get the # of the vector we want to relocate in
    74 	cell vector_index = array[i];			
    75 	//get the real address of this vector
    76 	char *real_address = (char *)array + (vector_index * sizeof(cell)) + old_offsets[vector_index];
    77 	//calc and store the new distance offset
    78 	array[i] = real_address - ( (char *)array + (vector_index + sizeof(cell)) )
    79  }
    80 
    81  Note that the inner expression can be heavily reduced; it is expanded for readability.
    82  **********************************/
    83 
    84 enum SortOrder
    85 {
    86 	Sort_Ascending = 0,
    87 	Sort_Descending = 1,
    88 };
    89 
    90 int sort_ints_asc(const void *int1, const void *int2)
    91 {
    92 	return (*(int *)int1) - (*(int *)int2);
    93 }
    94 
    95 int sort_ints_desc(const void *int1, const void *int2)
    96 {
    97 	return (*(int *)int2) - (*(int *)int1);
    98 }
    99 
   100 static cell_t sm_SortIntegers(IPluginContext *pContext, const cell_t *params)
   101 {
   102 	cell_t *array;
   103 	cell_t array_size = params[2];
   104 	cell_t type = params[3];
   105 
   106 	pContext->LocalToPhysAddr(params[1], &array);
   107 
   108 	if (type == Sort_Ascending)
   109 	{
   110 		qsort(array, array_size, sizeof(cell_t), sort_ints_asc);
   111 	} else {
   112 		qsort(array, array_size, sizeof(cell_t), sort_ints_desc);
   113 	}
   114 
   115 	return 1;
   116 }
   117 
   118 int sort_floats_asc(const void *float1, const void *float2)
   119 {
   120 	float r1 = *(float *)float1;
   121 	float r2 = *(float *)float2;
   122 	
   123 	if (r1 < r2)
   124 	{
   125 		return -1;
   126 	} else if (r2 < r1) {
   127 		return 1;
   128 	} else {
   129 		return 0;
   130 	}
   131 }
   132 
   133 int sort_floats_desc(const void *float1, const void *float2)
   134 {
   135 	float r1 = *(float *)float1;
   136 	float r2 = *(float *)float2;
   137 
   138 	if (r1 < r2)
   139 	{
   140 		return 1;
   141 	} else if (r2 < r1) {
   142 		return -1;
   143 	} else {
   144 		return 0;
   145 	}
   146 }
   147 
   148 static cell_t sm_SortFloats(IPluginContext *pContext, const cell_t *params)
   149 {
   150 	cell_t *array;
   151 	cell_t array_size = params[2];
   152 	cell_t type = params[3];
   153 
   154 	pContext->LocalToPhysAddr(params[1], &array);
   155 
   156 	if (type == Sort_Ascending)
   157 	{
   158 		qsort(array, array_size, sizeof(cell_t), sort_floats_asc);
   159 	} else {
   160 		qsort(array, array_size, sizeof(cell_t), sort_floats_desc);
   161 	}
   162 
   163 	return 1;
   164 }
   165 
   166 static cell_t *g_CurStringArray = NULL;
   167 static cell_t *g_CurRebaseMap = NULL;
   168 
   169 int sort_strings_asc(const void *blk1, const void *blk2)
   170 {
   171 	cell_t reloc1 = *(cell_t *)blk1;
   172 	cell_t reloc2 = *(cell_t *)blk2;
   173 	
   174 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   175 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   176 
   177 	return strcmp(str1, str2);
   178 }
   179 
   180 int sort_strings_desc(const void *blk1, const void *blk2)
   181 {
   182 	cell_t reloc1 = *(cell_t *)blk1;
   183 	cell_t reloc2 = *(cell_t *)blk2;
   184 
   185 	char *str1 = ((char *)(&g_CurStringArray[reloc1]) + g_CurRebaseMap[reloc1]);
   186 	char *str2 = ((char *)(&g_CurStringArray[reloc2]) + g_CurRebaseMap[reloc2]);
   187 
   188 	return strcmp(str2, str1);
   189 }
   190 
   191 static cell_t sm_SortStrings(IPluginContext *pContext, const cell_t *params)
   192 {
   193 	cell_t *array;
   194 	cell_t array_size = params[2];
   195 	cell_t type = params[3];
   196 
   197 	pContext->LocalToPhysAddr(params[1], &array);
   198 
   199 	/** HACKHACK - back up the old indices, replace the indices with something easier */
   200 	cell_t amx_addr, *phys_addr;
   201 	int err;
   202 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   203 	{
   204 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   205 	}
   206 
   207 	g_CurStringArray = array;
   208 	g_CurRebaseMap = phys_addr;
   209 
   210 	for (int i=0; i<array_size; i++)
   211 	{
   212 		phys_addr[i] = array[i];
   213 		array[i] = i;
   214 	}
   215 
   216 	if (type == Sort_Ascending)
   217 	{
   218 		qsort(array, array_size, sizeof(cell_t), sort_strings_asc);
   219 	} else {
   220 		qsort(array, array_size, sizeof(cell_t), sort_strings_desc);
   221 	}
   222 
   223 	/* END HACKHACK - restore what we damaged so Pawn doesn't throw up.
   224 	 * We'll browse through each index of the array and patch up the distance.
   225 	 */
   226 	for (int i=0; i<array_size; i++)
   227 	{
   228 		/* Compute the final address of the old array and subtract the new location.
   229 		 * This is the fixed up distance.
   230 		 */
   231 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   232 	}
   233 
   234 	pContext->HeapPop(amx_addr);
   235 
   236 	g_CurStringArray = NULL;
   237 	g_CurRebaseMap = NULL;
   238 
   239 	return 1;
   240 }
   241 
   242 struct sort_info
   243 {
   244 	IPluginFunction *pFunc;
   245 	Handle_t hndl;
   246 	cell_t array_addr;
   247 	cell_t *array_base;
   248 	cell_t *array_remap;
   249 };
   250 
   251 sort_info g_SortInfo;
   252 
   253 int sort1d_amx_custom(const void *elem1, const void *elem2)
   254 {
   255 	cell_t c1 = *(cell_t *)elem1;
   256 	cell_t c2 = *(cell_t *)elem2;
   257 
   258 	cell_t result = 0;
   259 	IPluginFunction *pf = g_SortInfo.pFunc;
   260 	pf->PushCell(c1);
   261 	pf->PushCell(c2);
   262 	pf->PushCell(g_SortInfo.array_addr);
   263 	pf->PushCell(g_SortInfo.hndl);
   264 	pf->Execute(&result);
   265 
   266 	return result;
   267 }
   268 
   269 static cell_t sm_SortCustom1D(IPluginContext *pContext, const cell_t *params)
   270 {
   271 	cell_t *array;
   272 	cell_t array_size = params[2];
   273 
   274 	IPluginFunction *pFunction = pContext->GetFunctionById(params[3]);
   275 	if (!pFunction)
   276 	{
   277 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   278 	}
   279 
   280 	pContext->LocalToPhysAddr(params[1], &array);
   281 
   282 	sort_info oldinfo = g_SortInfo;
   283 
   284 	
   285 	g_SortInfo.hndl = params[4];
   286 	g_SortInfo.array_addr = params[1];
   287 	g_SortInfo.array_remap = NULL;
   288 	g_SortInfo.array_base = NULL;
   289 	g_SortInfo.pFunc = pFunction;
   290 
   291 	qsort(array, array_size, sizeof(cell_t), sort1d_amx_custom);
   292 
   293 	g_SortInfo = oldinfo;
   294 
   295 	return 1;
   296 }
   297 
   298 int sort2d_amx_custom(const void *elem1, const void *elem2)
   299 {
   300 	cell_t c1 = *(cell_t *)elem1;
   301 	cell_t c2 = *(cell_t *)elem2;
   302 
   303 	cell_t c1_addr = g_SortInfo.array_addr + (c1 * sizeof(cell_t)) + g_SortInfo.array_remap[c1];
   304 	cell_t c2_addr = g_SortInfo.array_addr + (c2 * sizeof(cell_t)) + g_SortInfo.array_remap[c2];
   305 
   306 	IPluginContext *pContext = g_SortInfo.pFunc->GetParentContext();
   307 	cell_t *c1_r, *c2_r;
   308 	pContext->LocalToPhysAddr(c1_addr, &c1_r);
   309 	pContext->LocalToPhysAddr(c2_addr, &c2_r);
   310 
   311 	cell_t result = 0;
   312 	g_SortInfo.pFunc->PushCell(c1_addr);
   313 	g_SortInfo.pFunc->PushCell(c2_addr);
   314 	g_SortInfo.pFunc->PushCell(g_SortInfo.array_addr);
   315 	g_SortInfo.pFunc->PushCell(g_SortInfo.hndl);
   316 	g_SortInfo.pFunc->Execute(&result);
   317 
   318 	return result;
   319 }
   320 
   321 static cell_t sm_SortCustom2D(IPluginContext *pContext, const cell_t *params)
   322 {
   323 	cell_t *array;
   324 	cell_t array_size = params[2];
   325 	IPluginFunction *pFunction;
   326 
   327 	pContext->LocalToPhysAddr(params[1], &array);
   328 
   329 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   330 	{
   331 		return pContext->ThrowNativeError("Function %x is not a valid function", params[3]);
   332 	}
   333 
   334 	/** back up the old indices, replace the indices with something easier */
   335 	cell_t amx_addr, *phys_addr;
   336 	int err;
   337 	if ((err=pContext->HeapAlloc(array_size, &amx_addr, &phys_addr)) != SP_ERROR_NONE)
   338 	{
   339 		pContext->ThrowNativeErrorEx(err, "Ran out of memory to sort");
   340 		return 0;
   341 	}
   342 
   343 	sort_info oldinfo = g_SortInfo;
   344 
   345 	g_SortInfo.pFunc = pFunction;
   346 	g_SortInfo.hndl = params[4];
   347 	g_SortInfo.array_addr = params[1];
   348 	
   349 	/** Same process as in strings, back up the old indices for later fixup */
   350 	g_SortInfo.array_base = array;
   351 	g_SortInfo.array_remap = phys_addr;
   352 	
   353 	for (int i=0; i<array_size; i++)
   354 	{
   355 		phys_addr[i] = array[i];
   356 		array[i] = i;
   357 	}
   358 
   359 	qsort(array, array_size, sizeof(cell_t), sort2d_amx_custom);
   360 
   361 	/** Fixup process! */
   362 	for (int i=0; i<array_size; i++)
   363 	{
   364 		/* Compute the final address of the old array and subtract the new location.
   365 		 * This is the fixed up distance.
   366 		 */
   367 		array[i] = ((char *)&array[array[i]] + phys_addr[array[i]]) - (char *)&array[i];
   368 	}
   369 
   370 	pContext->HeapPop(amx_addr);
   371 
   372 	g_SortInfo = oldinfo;
   373 	
   374 	return 1;
   375 }
   376 
   377 REGISTER_NATIVES(sortNatives)
   378 {
   379 	{"SortIntegers",			sm_SortIntegers},
   380 	{"SortFloats",				sm_SortFloats},
   381 	{"SortStrings",				sm_SortStrings},
   382 	{"SortCustom1D",			sm_SortCustom1D},
   383 	{"SortCustom2D",			sm_SortCustom2D},
   384 	{NULL,						NULL},
   385 };