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