plugins/adminmenu/dynamicmenu.sp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Nov 30 13:09:30 2010 -0500 (2010-11-30)
changeset 3162 d8f90b56529f
parent 2920 1d5645ab7ae0
child 3808 b5f4ea4d3736
permissions -rw-r--r--
Fixed BanClient always banning by IP address (bug 4626, r=fyren).
     1 
     2 #define NAME_LENGTH 64
     3 #define CMD_LENGTH 255
     4 
     5 #define ARRAY_STRING_LENGTH 32
     6 
     7 enum GroupCommands
     8 {
     9 	Handle:groupListName,
    10 	Handle:groupListCommand	
    11 };
    12 
    13 new g_groupList[GroupCommands];
    14 new g_groupCount;
    15 
    16 new Handle:g_configParser = INVALID_HANDLE;
    17 
    18 enum Places
    19 {
    20 	Place_Category,
    21 	Place_Item,
    22 	Place_ReplaceNum	
    23 };
    24 
    25 new String:g_command[MAXPLAYERS+1][CMD_LENGTH];
    26 new g_currentPlace[MAXPLAYERS+1][Places];
    27 
    28 /**
    29  * What to put in the 'info' menu field (for PlayerList and Player_Team menus only)
    30  */
    31 enum PlayerMethod
    32 {		
    33 	ClientId,				/** Client id number ( 1 - Maxplayers) */
    34 	UserId,					/** Client userid */
    35 	Name,					/** Client Name */
    36 	SteamId,				/** Client Steamid */
    37 	IpAddress,				/** Client's Ip Address */
    38 	UserId2					/** Userid (not prefixed with #) */
    39 };
    40 
    41 enum ExecuteType
    42 {
    43 	Execute_Player,
    44 	Execute_Server
    45 }
    46 
    47 enum SubMenu_Type
    48 {
    49 	SubMenu_Group,
    50 	SubMenu_GroupPlayer,
    51 	SubMenu_Player,
    52 	SubMenu_MapCycle,
    53 	SubMenu_List,
    54 	SubMenu_OnOff	
    55 }
    56 
    57 enum Item
    58 {
    59 	String:Item_cmd[256],
    60 	ExecuteType:Item_execute,
    61 	Handle:Item_submenus
    62 }
    63 
    64 enum Submenu
    65 {
    66 	SubMenu_Type:Submenu_type,
    67 	String:Submenu_title[32],
    68 	PlayerMethod:Submenu_method,
    69 	Submenu_listcount,
    70 	Handle:Submenu_listdata
    71 }
    72 
    73 new Handle:g_DataArray;
    74 
    75 BuildDynamicMenu()
    76 {
    77 	new itemInput[Item];
    78 	g_DataArray = CreateArray(sizeof(itemInput));
    79 	
    80 	new String:executeBuffer[32];
    81 	
    82 	new Handle:kvMenu;
    83 	kvMenu = CreateKeyValues("Commands");
    84 	KvSetEscapeSequences(kvMenu, true); 
    85 	
    86 	new String:file[256];
    87 	
    88 	/* As a compatibility shim, we use the old file if it exists. */
    89 	BuildPath(Path_SM, file, 255, "configs/dynamicmenu/menu.ini");
    90 	if (FileExists(file))
    91 	{
    92 		LogError("Warning! configs/dynamicmenu/menu.ini is now configs/adminmenu_custom.txt.");
    93 		LogError("Read the 1.0.2 release notes, as the dynamicmenu folder has been removed.");
    94 	}
    95 	else
    96 	{
    97 		BuildPath(Path_SM, file, 255, "configs/adminmenu_custom.txt");
    98 	}
    99 	
   100 	FileToKeyValues(kvMenu, file);
   101 	
   102 	new String:name[NAME_LENGTH];
   103 	new String:buffer[NAME_LENGTH];
   104 		
   105 	if (!KvGotoFirstSubKey(kvMenu))
   106 	{
   107 		return;
   108 	}
   109 	
   110 	decl String:admin[30];
   111 	
   112 	new TopMenuObject:categoryId;
   113 	
   114 	do
   115 	{		
   116 		KvGetSectionName(kvMenu, buffer, sizeof(buffer));
   117 
   118 		KvGetString(kvMenu, "admin", admin, sizeof(admin),"sm_admin");
   119 				
   120 		if ((categoryId =FindTopMenuCategory(hAdminMenu, buffer)) == INVALID_TOPMENUOBJECT)
   121 		{
   122 			categoryId = AddToTopMenu(hAdminMenu,
   123 							buffer,
   124 							TopMenuObject_Category,
   125 							DynamicMenuCategoryHandler,
   126 							INVALID_TOPMENUOBJECT,
   127 							admin,
   128 							ADMFLAG_GENERIC,
   129 							name);
   130 
   131 		}
   132 
   133 		decl String:category_name[NAME_LENGTH];
   134 		strcopy(category_name, sizeof(category_name), buffer);
   135 		
   136 		if (!KvGotoFirstSubKey(kvMenu))
   137 		{
   138 			return;
   139 		}
   140 		
   141 		do
   142 		{		
   143 			KvGetSectionName(kvMenu, buffer, sizeof(buffer));
   144 			
   145 			KvGetString(kvMenu, "admin", admin, sizeof(admin),"");
   146 			
   147 			if (admin[0] == '\0')
   148 			{
   149 				//No 'admin' keyvalue was found
   150 				//Use the first argument of the 'cmd' string instead
   151 				
   152 				decl String:temp[64];
   153 				KvGetString(kvMenu, "cmd", temp, sizeof(temp),"");
   154 				
   155 				BreakString(temp, admin, sizeof(admin));
   156 			}
   157 			
   158 			
   159 			KvGetString(kvMenu, "cmd", itemInput[Item_cmd], sizeof(itemInput[Item_cmd]));	
   160 			KvGetString(kvMenu, "execute", executeBuffer, sizeof(executeBuffer));
   161 			
   162 			if (StrEqual(executeBuffer, "server"))
   163 			{
   164 				itemInput[Item_execute] = Execute_Server;
   165 			}
   166 			else //assume player type execute
   167 			{
   168 				itemInput[Item_execute] = Execute_Player;
   169 			}
   170   							
   171   			/* iterate all submenus and load data into itemInput[Item_submenus] (adt array handle) */
   172   			
   173   			new count = 1;
   174   			decl String:countBuffer[10] = "1";
   175   			
   176   			decl String:inputBuffer[48];
   177   			
   178   			while (KvJumpToKey(kvMenu, countBuffer))
   179   			{
   180 	  			new submenuInput[Submenu];
   181 	  			
   182 	  			if (count == 1)
   183 	  			{
   184 		  			itemInput[Item_submenus] = CreateArray(sizeof(submenuInput));	
   185 	  			}
   186 	  			
   187 	  			KvGetString(kvMenu, "type", inputBuffer, sizeof(inputBuffer));
   188 	  			
   189 	  			if (strncmp(inputBuffer,"group",5)==0)
   190 				{	
   191 					if (StrContains(inputBuffer, "player") != -1)
   192 					{			
   193 						submenuInput[Submenu_type] = SubMenu_GroupPlayer;
   194 					}
   195 					else
   196 					{
   197 						submenuInput[Submenu_type] = SubMenu_Group;
   198 					}
   199 				}			
   200 				else if (StrEqual(inputBuffer,"mapcycle"))
   201 				{
   202 					submenuInput[Submenu_type] = SubMenu_MapCycle;
   203 					
   204 					KvGetString(kvMenu, "path", inputBuffer, sizeof(inputBuffer),"mapcycle.txt");
   205 					
   206 					submenuInput[Submenu_listdata] = CreateDataPack();
   207 					WritePackString(submenuInput[Submenu_listdata], inputBuffer);
   208 					ResetPack(submenuInput[Submenu_listdata]);
   209 				}
   210 				else if (StrContains(inputBuffer, "player") != -1)
   211 				{			
   212 					submenuInput[Submenu_type] = SubMenu_Player;
   213 				}
   214 				else if (StrEqual(inputBuffer,"onoff"))
   215 				{
   216 					submenuInput[Submenu_type] = SubMenu_OnOff;
   217 				}		
   218 				else //assume 'list' type
   219 				{
   220 					submenuInput[Submenu_type] = SubMenu_List;
   221 					
   222 					submenuInput[Submenu_listdata] = CreateDataPack();
   223 					
   224 					new String:temp[6];
   225 					new String:value[64];
   226 					new String:text[64];
   227 					new String:subadm[30];	//  same as "admin", cf. line 110
   228 					new i=1;
   229 					new bool:more = true;
   230 					
   231 					new listcount = 0;
   232 								
   233 					do
   234 					{
   235 						Format(temp,3,"%i",i);
   236 						KvGetString(kvMenu, temp, value, sizeof(value), "");
   237 						
   238 						Format(temp,5,"%i.",i);
   239 						KvGetString(kvMenu, temp, text, sizeof(text), value);
   240 						
   241 						Format(temp,5,"%i*",i);
   242 						KvGetString(kvMenu, temp, subadm, sizeof(subadm),"");	
   243 						
   244 						if (value[0]=='\0')
   245 						{
   246 							more = false;
   247 						}
   248 						else
   249 						{
   250 							listcount++;
   251 							WritePackString(submenuInput[Submenu_listdata], value);
   252 							WritePackString(submenuInput[Submenu_listdata], text);
   253 							WritePackString(submenuInput[Submenu_listdata], subadm);
   254 						}
   255 						
   256 						i++;
   257 										
   258 					} while (more);
   259 					
   260 					ResetPack(submenuInput[Submenu_listdata]);
   261 					submenuInput[Submenu_listcount] = listcount;
   262 				}
   263 				
   264 				if ((submenuInput[Submenu_type] == SubMenu_Player) || (submenuInput[Submenu_type] == SubMenu_GroupPlayer))
   265 				{
   266 					KvGetString(kvMenu, "method", inputBuffer, sizeof(inputBuffer));
   267 					
   268 					if (StrEqual(inputBuffer, "clientid"))
   269 					{
   270 						submenuInput[Submenu_method] = ClientId;
   271 					}
   272 					else if (StrEqual(inputBuffer, "steamid"))
   273 					{
   274 						submenuInput[Submenu_method] = SteamId;
   275 					}
   276 					else if (StrEqual(inputBuffer, "userid2"))
   277 					{
   278 						submenuInput[Submenu_method] = UserId2;
   279 					}
   280 					else if (StrEqual(inputBuffer, "userid"))
   281 					{
   282 						submenuInput[Submenu_method] = UserId;
   283 					}
   284 					else if (StrEqual(inputBuffer, "ip"))
   285 					{
   286 						submenuInput[Submenu_method] = IpAddress;
   287 					}
   288 					else
   289 					{
   290 						submenuInput[Submenu_method] = Name;
   291 					}				
   292 				}
   293 				
   294 				KvGetString(kvMenu, "title", inputBuffer, sizeof(inputBuffer));
   295 				strcopy(submenuInput[Submenu_title], sizeof(submenuInput[Submenu_title]), inputBuffer);
   296 	  			
   297 	  			count++;
   298 	  			Format(countBuffer, sizeof(countBuffer), "%i", count);
   299 	  			
   300 	  			PushArrayArray(itemInput[Item_submenus], submenuInput[0]);
   301 	  		
   302 	  			KvGoBack(kvMenu);	
   303   			}
   304   			
   305   			/* Save this entire item into the global items array and add it to the menu */
   306   			
   307   			new location = PushArrayArray(g_DataArray, itemInput[0]);
   308 			
   309 			decl String:locString[10];
   310 			IntToString(location, locString, sizeof(locString));
   311 
   312 			if (AddToTopMenu(hAdminMenu,
   313 				buffer,
   314 				TopMenuObject_Item,
   315 				DynamicMenuItemHandler,
   316   				categoryId,
   317   				admin,
   318   				ADMFLAG_GENERIC,
   319   				locString) == INVALID_TOPMENUOBJECT)
   320 			{
   321 				LogError("Duplicate command name \"%s\" in adminmenu_custom.txt category \"%s\"", buffer, category_name);
   322 			}
   323 		
   324 		} while (KvGotoNextKey(kvMenu));
   325 		
   326 		KvGoBack(kvMenu);
   327 		
   328 	} while (KvGotoNextKey(kvMenu));
   329 	
   330 	CloseHandle(kvMenu);
   331 }
   332 
   333 ParseConfigs()
   334 {
   335 	if (g_configParser == INVALID_HANDLE)
   336 	{
   337 		g_configParser = SMC_CreateParser();
   338 	}
   339 	
   340 	SMC_SetReaders(g_configParser, NewSection, KeyValue, EndSection);
   341 	
   342 	if (g_groupList[groupListName] != INVALID_HANDLE)
   343 	{
   344 		CloseHandle(g_groupList[groupListName]);
   345 	}
   346 	
   347 	if (g_groupList[groupListCommand] != INVALID_HANDLE)
   348 	{
   349 		CloseHandle(g_groupList[groupListCommand]);
   350 	}
   351 	
   352 	g_groupList[groupListName] = CreateArray(ARRAY_STRING_LENGTH);
   353 	g_groupList[groupListCommand] = CreateArray(ARRAY_STRING_LENGTH);
   354 	
   355 	decl String:configPath[256];
   356 	BuildPath(Path_SM, configPath, sizeof(configPath), "configs/dynamicmenu/adminmenu_grouping.txt");
   357 	if (FileExists(configPath))
   358 	{
   359 		LogError("Warning! configs/dynamicmenu/adminmenu_grouping.txt is now configs/adminmenu_grouping.txt.");
   360 		LogError("Read the 1.0.2 release notes, as the dynamicmenu folder has been removed.");
   361 	}
   362 	else
   363 	{
   364 		BuildPath(Path_SM, configPath, sizeof(configPath), "configs/adminmenu_grouping.txt");
   365 	}
   366 	
   367 	if (!FileExists(configPath))
   368 	{
   369 		LogError("Unable to locate admin menu groups file: %s", configPath);
   370 			
   371 		return;		
   372 	}
   373 	
   374 	new line;
   375 	new SMCError:err = SMC_ParseFile(g_configParser, configPath, line);
   376 	if (err != SMCError_Okay)
   377 	{
   378 		decl String:error[256];
   379 		SMC_GetErrorString(err, error, sizeof(error));
   380 		LogError("Could not parse file (line %d, file \"%s\"):", line, configPath);
   381 		LogError("Parser encountered error: %s", error);
   382 	}
   383 	
   384 	return;
   385 }
   386 
   387 public SMCResult:NewSection(Handle:smc, const String:name[], bool:opt_quotes)
   388 {
   389 
   390 }
   391 
   392 public SMCResult:KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes)
   393 {
   394 	PushArrayString(g_groupList[groupListName], key);
   395 	PushArrayString(g_groupList[groupListCommand], value);
   396 }
   397 
   398 public SMCResult:EndSection(Handle:smc)
   399 {
   400 	g_groupCount = GetArraySize(g_groupList[groupListName]);
   401 }
   402 
   403 public DynamicMenuCategoryHandler(Handle:topmenu, 
   404 						TopMenuAction:action,
   405 						TopMenuObject:object_id,
   406 						param,
   407 						String:buffer[],
   408 						maxlength)
   409 {
   410 	if ((action == TopMenuAction_DisplayTitle) || (action == TopMenuAction_DisplayOption))
   411 	{
   412 		GetTopMenuObjName(topmenu, object_id, buffer, maxlength);
   413 	}
   414 }
   415 
   416 public DynamicMenuItemHandler(Handle:topmenu, 
   417 					  TopMenuAction:action,
   418 					  TopMenuObject:object_id,
   419 					  param,
   420 					  String:buffer[],
   421 					  maxlength)
   422 {
   423 	if (action == TopMenuAction_DisplayOption)
   424 	{
   425 		GetTopMenuObjName(topmenu, object_id, buffer, maxlength);
   426 	}
   427 	else if (action == TopMenuAction_SelectOption)
   428 	{	
   429 		new String:locString[10];
   430 		GetTopMenuInfoString(topmenu, object_id, locString, sizeof(locString));
   431 		
   432 		new location = StringToInt(locString);
   433 		
   434 		new output[Item];
   435 		GetArrayArray(g_DataArray, location, output[0]);
   436 		
   437 		strcopy(g_command[param], sizeof(g_command[]), output[Item_cmd]);
   438 					
   439 		g_currentPlace[param][Place_Item] = location;
   440 		g_currentPlace[param][Place_ReplaceNum] = 1;
   441 		
   442 		ParamCheck(param);
   443 	}
   444 }
   445 
   446 public ParamCheck(client)
   447 {
   448 	new String:buffer[6];
   449 	new String:buffer2[6];
   450 	
   451 	new outputItem[Item];
   452 	new outputSubmenu[Submenu];
   453 	
   454 	GetArrayArray(g_DataArray, g_currentPlace[client][Place_Item], outputItem[0]);
   455 		
   456 	if (g_currentPlace[client][Place_ReplaceNum] < 1)
   457 	{
   458 		g_currentPlace[client][Place_ReplaceNum] = 1;
   459 	}
   460 	
   461 	Format(buffer, 5, "#%i", g_currentPlace[client][Place_ReplaceNum]);
   462 	Format(buffer2, 5, "@%i", g_currentPlace[client][Place_ReplaceNum]);
   463 	
   464 	if (StrContains(g_command[client], buffer) != -1 || StrContains(g_command[client], buffer2) != -1)
   465 	{
   466 		GetArrayArray(outputItem[Item_submenus], g_currentPlace[client][Place_ReplaceNum] - 1, outputSubmenu[0]);
   467 		
   468 		new Handle:itemMenu = CreateMenu(Menu_Selection);
   469 		SetMenuExitBackButton(itemMenu, true);
   470 			
   471 		if ((outputSubmenu[Submenu_type] == SubMenu_Group) || (outputSubmenu[Submenu_type] == SubMenu_GroupPlayer))
   472 		{	
   473 			decl String:nameBuffer[ARRAY_STRING_LENGTH];
   474 			decl String:commandBuffer[ARRAY_STRING_LENGTH];
   475 		
   476 			for (new i = 0; i<g_groupCount; i++)
   477 			{			
   478 				GetArrayString(g_groupList[groupListName], i, nameBuffer, sizeof(nameBuffer));
   479 				GetArrayString(g_groupList[groupListCommand], i, commandBuffer, sizeof(commandBuffer));
   480 				AddMenuItem(itemMenu, commandBuffer, nameBuffer);
   481 			}
   482 		}
   483 		
   484 		if (outputSubmenu[Submenu_type] == SubMenu_MapCycle)
   485 		{	
   486 			decl String:path[200];
   487 			ReadPackString(outputSubmenu[Submenu_listdata], path, sizeof(path));
   488 			ResetPack(outputSubmenu[Submenu_listdata]);
   489 		
   490 			new Handle:file = OpenFile(path, "rt");
   491 			new String:readData[128];
   492 			
   493 			if(file != INVALID_HANDLE)
   494 			{
   495 				while(!IsEndOfFile(file) && ReadFileLine(file, readData, sizeof(readData)))
   496 				{
   497 					TrimString(readData);
   498 					
   499 					if (IsMapValid(readData))
   500 					{
   501 						AddMenuItem(itemMenu, readData, readData);
   502 					}
   503 				}
   504 			}
   505 		}
   506 		else if ((outputSubmenu[Submenu_type] == SubMenu_Player) || (outputSubmenu[Submenu_type] == SubMenu_GroupPlayer))
   507 		{
   508 			new PlayerMethod:playermethod = outputSubmenu[Submenu_method];
   509 		
   510 			new String:nameBuffer[32];
   511 			new String:infoBuffer[32];
   512 			new String:temp[4];
   513 			
   514 			//loop through players. Add name as text and name/userid/steamid as info
   515 			for (new i=1; i<=MaxClients; i++)
   516 			{
   517 				if (IsClientInGame(i))
   518 				{			
   519 					GetClientName(i, nameBuffer, 31);
   520 					
   521 					switch (playermethod)
   522 					{
   523 						case UserId:
   524 						{
   525 							new userid = GetClientUserId(i);
   526 							Format(infoBuffer, sizeof(infoBuffer), "#%i", userid);
   527 							AddMenuItem(itemMenu, infoBuffer, nameBuffer);	
   528 						}
   529 						case UserId2:
   530 						{
   531 							new userid = GetClientUserId(i);
   532 							Format(infoBuffer, sizeof(infoBuffer), "%i", userid);
   533 							AddMenuItem(itemMenu, infoBuffer, nameBuffer);							
   534 						}
   535 						case SteamId:
   536 						{
   537 							GetClientAuthString(i, infoBuffer, sizeof(infoBuffer));
   538 							AddMenuItem(itemMenu, infoBuffer, nameBuffer);							
   539 						}	
   540 						case IpAddress:
   541 						{
   542 							GetClientIP(i, infoBuffer, sizeof(infoBuffer));
   543 							AddMenuItem(itemMenu, infoBuffer, nameBuffer);							
   544 						}
   545 						case Name:
   546 						{
   547 							AddMenuItem(itemMenu, nameBuffer, nameBuffer);
   548 						}	
   549 						default: //assume client id
   550 						{
   551 							Format(temp,3,"%i",i);
   552 							AddMenuItem(itemMenu, temp, nameBuffer);						
   553 						}								
   554 					}
   555 				}
   556 			}
   557 		}
   558 		else if (outputSubmenu[Submenu_type] == SubMenu_OnOff)
   559 		{
   560 			AddMenuItem(itemMenu, "1", "On");
   561 			AddMenuItem(itemMenu, "0", "Off");
   562 		}		
   563 		else
   564 		{
   565 			new String:value[64];
   566 			new String:text[64];
   567 					
   568 			new String:admin[NAME_LENGTH];
   569 			
   570 			for (new i=0; i<outputSubmenu[Submenu_listcount]; i++)
   571 			{
   572 				ReadPackString(outputSubmenu[Submenu_listdata], value, sizeof(value));
   573 				ReadPackString(outputSubmenu[Submenu_listdata], text, sizeof(text));
   574 				ReadPackString(outputSubmenu[Submenu_listdata], admin, sizeof(admin));
   575 				
   576 				if (CheckCommandAccess(client, admin, 0))
   577 				{
   578 					AddMenuItem(itemMenu, value, text);
   579 				}
   580 			}
   581 			
   582 			ResetPack(outputSubmenu[Submenu_listdata]);	
   583 		}
   584 		
   585 		SetMenuTitle(itemMenu, outputSubmenu[Submenu_title]);
   586 		
   587 		DisplayMenu(itemMenu, client, MENU_TIME_FOREVER);
   588 	}
   589 	else
   590 	{	
   591 		//nothing else need to be done. Run teh command.
   592 		
   593 		DisplayTopMenu(hAdminMenu, client, TopMenuPosition_LastCategory);
   594 		
   595 		decl String:unquotedCommand[CMD_LENGTH];
   596 		UnQuoteString(g_command[client], unquotedCommand, sizeof(unquotedCommand), "#@");
   597 		
   598 		if (outputItem[Item_execute] == Execute_Player) // assume 'player' type execute option
   599 		{
   600 			FakeClientCommand(client, unquotedCommand);
   601 		}
   602 		else // assume 'server' type execute option
   603 		{
   604 			InsertServerCommand(unquotedCommand);
   605 			ServerExecute();
   606 		}
   607 
   608 		g_command[client][0] = '\0';
   609 		g_currentPlace[client][Place_ReplaceNum] = 1;
   610 	}
   611 }
   612 
   613 public Menu_Selection(Handle:menu, MenuAction:action, param1, param2)
   614 {
   615 	if (action == MenuAction_End)
   616 	{
   617 		CloseHandle(menu);
   618 	}
   619 	
   620 	if (action == MenuAction_Select)
   621 	{
   622 		new String:unquotedinfo[NAME_LENGTH];
   623  
   624 		/* Get item info */
   625 		new bool:found = GetMenuItem(menu, param2, unquotedinfo, sizeof(unquotedinfo));
   626 		
   627 		if (!found)
   628 		{
   629 			return;
   630 		}
   631 		
   632 		new String:info[NAME_LENGTH*2+1];
   633 		QuoteString(unquotedinfo, info, sizeof(info), "#@");
   634 		
   635 		
   636 		new String:buffer[6];
   637 		new String:infobuffer[NAME_LENGTH+2];
   638 		Format(infobuffer, sizeof(infobuffer), "\"%s\"", info);
   639 		
   640 		Format(buffer, 5, "#%i", g_currentPlace[param1][Place_ReplaceNum]);
   641 		ReplaceString(g_command[param1], sizeof(g_command[]), buffer, infobuffer);
   642 		//replace #num with the selected option (quoted)
   643 		
   644 		Format(buffer, 5, "@%i", g_currentPlace[param1][Place_ReplaceNum]);
   645 		ReplaceString(g_command[param1], sizeof(g_command[]), buffer, info);
   646 		//replace @num with the selected option (unquoted)
   647 		
   648 		// Increment the parameter counter.
   649 		g_currentPlace[param1][Place_ReplaceNum]++;
   650 		
   651 		ParamCheck(param1);
   652 	}
   653 	
   654 	if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack)
   655 	{
   656 		//client exited we should go back to submenu i think
   657 		DisplayTopMenu(hAdminMenu, param1, TopMenuPosition_LastCategory);
   658 	}
   659 }
   660 
   661 
   662 stock bool:QuoteString(String:input[], String:output[], maxlen, String:quotechars[])
   663 {
   664 	new count = 0;
   665 	new len = strlen(input);
   666 	
   667 	for (new i=0; i<len; i++)
   668 	{
   669 		output[count] = input[i];
   670 		count++;
   671 		
   672 		if (count >= maxlen)
   673 		{
   674 			/* Null terminate for safety */
   675 			output[maxlen-1] = 0;
   676 			return false;	
   677 		}
   678 		
   679 		if (FindCharInString(quotechars, input[i]) != -1 || input[i] == '\\')
   680 		{
   681 			/* This char needs escaping */
   682 			output[count] = '\\';
   683 			count++;
   684 			
   685 			if (count >= maxlen)
   686 			{
   687 				/* Null terminate for safety */
   688 				output[maxlen-1] = 0;
   689 				return false;	
   690 			}		
   691 		}
   692 	}
   693 	
   694 	output[count] = 0;
   695 	
   696 	return true;
   697 }
   698 
   699 stock bool:UnQuoteString(String:input[], String:output[], maxlen, String:quotechars[])
   700 {
   701 	new count = 1;
   702 	new len = strlen(input);
   703 	
   704 	output[0] = input[0];
   705 	
   706 	for (new i=1; i<len; i++)
   707 	{
   708 		output[count] = input[i];
   709 		count++;
   710 		
   711 		if (input[i+1] == '\\' && (input[i] == '\\' || FindCharInString(quotechars, input[i]) != -1))
   712 		{
   713 			/* valid quotechar followed by a backslash - Skip */
   714 			i++; 
   715 		}
   716 		
   717 		if (count >= maxlen)
   718 		{
   719 			/* Null terminate for safety */
   720 			output[maxlen-1] = 0;
   721 			return false;	
   722 		}
   723 	}
   724 	
   725 	output[count] = 0;
   726 	
   727 	return true;
   728 }