- massive overhaul of the voting subsystem
authorDavid Anderson <dvander@alliedmods.net>
Wed Aug 08 02:27:10 2007 +0000 (2007-08-08)
changeset 126927397584df9c
parent 1268 9360f5ea2289
child 1270 a6e3a3b8b7f3
- massive overhaul of the voting subsystem
- NOTE: menu API compatibility was COMPLETELY broken for C++!
- cleaned up various aspects of the menu API
- implemented new extended vote results callback
- extended the debug reporter a tiny bit
core/DebugReporter.cpp
core/DebugReporter.h
core/Makefile
core/MenuManager.cpp
core/MenuManager.h
core/MenuStyle_Base.cpp
core/MenuStyle_Base.h
core/MenuStyle_Radio.cpp
core/MenuStyle_Radio.h
core/MenuStyle_Valve.cpp
core/MenuStyle_Valve.h
core/MenuVoting.cpp
core/MenuVoting.h
core/msvc8/sourcemod_mm.vcproj
core/smn_menus.cpp
core/vm/sp_vm_basecontext.cpp
core/vm/sp_vm_engine.cpp
core/vm/sp_vm_engine.h
plugins/include/menus.inc
public/IMenuManager.h
     1.1 --- a/core/DebugReporter.cpp	Tue Aug 07 22:11:00 2007 +0000
     1.2 +++ b/core/DebugReporter.cpp	Wed Aug 08 02:27:10 2007 +0000
     1.3 @@ -33,14 +33,56 @@
     1.4  #include "DebugReporter.h"
     1.5  #include "Logger.h"
     1.6  #include "PluginSys.h"
     1.7 +#include "sm_stringutil.h"
     1.8  
     1.9  DebugReport g_DbgReporter;
    1.10  
    1.11 +/* I'm really lazy.  This should probably be exported to ISourcePawnEngine someday,
    1.12 + * but we need to make sure the JIT will deal with the version bump.
    1.13 + */
    1.14 +extern const char *GetSourcePawnErrorMessage(int error);
    1.15 +
    1.16  void DebugReport::OnSourceModAllInitialized()
    1.17  {
    1.18  	g_pSourcePawn->SetDebugListener(this);
    1.19  }
    1.20  
    1.21 +void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...)
    1.22 +{
    1.23 +	va_list ap;
    1.24 +	char buffer[512];
    1.25 +
    1.26 +	va_start(ap, message);
    1.27 +	UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
    1.28 +	va_end(ap);
    1.29 +
    1.30 +	const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
    1.31 +	const char *error = GetSourcePawnErrorMessage(err);
    1.32 +
    1.33 +	if (error)
    1.34 +	{
    1.35 +		g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
    1.36 +	} else {
    1.37 +		g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
    1.38 +	}
    1.39 +
    1.40 +	g_Logger.LogError("[SM] %s", buffer);
    1.41 +
    1.42 +	const char *func_name = NULL;
    1.43 +	if (func_idx != -1)
    1.44 +	{
    1.45 +		if (func_idx & 1)
    1.46 +		{
    1.47 +			func_idx >>= 1;
    1.48 +			sp_public_t *function;
    1.49 +			if (ctx->GetPublicByIndex(func_idx, &function) == SP_ERROR_NONE)
    1.50 +			{
    1.51 +				g_Logger.LogError("[SM] Unable to call function \"%s\" due to above errors.", function->name);
    1.52 +			}
    1.53 +		}
    1.54 +	}
    1.55 +}
    1.56 +
    1.57  void DebugReport::OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
    1.58  {
    1.59  	const char *lastname;
     2.1 --- a/core/DebugReporter.h	Tue Aug 07 22:11:00 2007 +0000
     2.2 +++ b/core/DebugReporter.h	Wed Aug 08 02:27:10 2007 +0000
     2.3 @@ -44,6 +44,7 @@
     2.4  	void OnSourceModAllInitialized();
     2.5  public: // IDebugListener
     2.6  	void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error);
     2.7 +	void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
     2.8  private:
     2.9  	int _GetPluginIndex(IPluginContext *ctx);
    2.10  };
     3.1 --- a/core/Makefile	Tue Aug 07 22:11:00 2007 +0000
     3.2 +++ b/core/Makefile	Wed Aug 08 02:27:10 2007 +0000
     3.3 @@ -24,7 +24,7 @@
     3.4  	MemoryUtils.cpp PlayerManager.cpp TextParsers.cpp TimerSys.cpp Translator.cpp UserMessages.cpp \
     3.5  	sm_autonatives.cpp sm_memtable.cpp sm_srvcmds.cpp sm_stringutil.cpp sm_trie.cpp \
     3.6  	sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
     3.7 -	MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp
     3.8 +	MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp
     3.9  OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
    3.10  	smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
    3.11  	smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp smn_handles.cpp smn_keyvalues.cpp \
     4.1 --- a/core/MenuManager.cpp	Tue Aug 07 22:11:00 2007 +0000
     4.2 +++ b/core/MenuManager.cpp	Wed Aug 08 02:27:10 2007 +0000
     4.3 @@ -34,6 +34,7 @@
     4.4  #include <stdarg.h>
     4.5  #include <stdlib.h>
     4.6  #include "MenuManager.h"
     4.7 +#include "MenuVoting.h"
     4.8  #include "sm_stringutil.h"
     4.9  #include "sourcemm_api.h"
    4.10  #include "PlayerManager.h"
    4.11 @@ -43,220 +44,10 @@
    4.12  #include "sourcemm_api.h"
    4.13  
    4.14  MenuManager g_Menus;
    4.15 +VoteMenuHandler s_VoteHandler;
    4.16  
    4.17  ConVar sm_menu_sounds("sm_menu_sounds", "1", 0, "Sets whether SourceMod menus play trigger sounds");
    4.18  
    4.19 -/*******************************
    4.20 - *******************************
    4.21 - ******** VOTE HANDLER *********
    4.22 - *******************************
    4.23 - *******************************/
    4.24 -
    4.25 -unsigned int VoteMenuHandler::GetMenuAPIVersion2()
    4.26 -{
    4.27 -	return m_pHandler->GetMenuAPIVersion2();
    4.28 -}
    4.29 -
    4.30 -bool VoteMenuHandler::IsVoteInProgress()
    4.31 -{
    4.32 -	return (m_pCurMenu != NULL);
    4.33 -}
    4.34 -
    4.35 -void VoteMenuHandler::InitializeVoting(IBaseMenu *menu)
    4.36 -{
    4.37 -	m_Items = menu->GetItemCount();
    4.38 -
    4.39 -	if (m_Votes.size() < (size_t)m_Items)
    4.40 -	{
    4.41 -		/* Only clear the items we need to... */
    4.42 -		size_t size = m_Votes.size();
    4.43 -		for (size_t i=0; i<size; i++)
    4.44 -		{
    4.45 -			m_Votes[i] = 0;
    4.46 -		}
    4.47 -		m_Votes.resize(m_Items, 0);
    4.48 -	} else {
    4.49 -		for (unsigned int i=0; i<m_Items; i++)
    4.50 -		{
    4.51 -			m_Votes[i] = 0;
    4.52 -		}
    4.53 -	}
    4.54 -
    4.55 -	m_pCurMenu = menu;
    4.56 -
    4.57 -	m_pHandler->OnMenuStart(m_pCurMenu);
    4.58 -}
    4.59 -
    4.60 -void VoteMenuHandler::StartVoting()
    4.61 -{
    4.62 -	m_bStarted = true;
    4.63 -
    4.64 -	m_pHandler->OnMenuVoteStart(m_pCurMenu);
    4.65 -
    4.66 -	/* By now we know how many clients were set.  
    4.67 -	 * If there are none, we should end IMMEDIATELY.
    4.68 -	 */
    4.69 -	if (m_Clients == 0)
    4.70 -	{
    4.71 -		EndVoting();
    4.72 -	}
    4.73 -}
    4.74 -
    4.75 -void VoteMenuHandler::DecrementPlayerCount()
    4.76 -{
    4.77 -	assert(m_Clients > 0);
    4.78 -
    4.79 -	m_Clients--;
    4.80 -
    4.81 -	if (m_bStarted && m_Clients == 0)
    4.82 -	{
    4.83 -		EndVoting();
    4.84 -	}
    4.85 -}
    4.86 -
    4.87 -void VoteMenuHandler::EndVoting()
    4.88 -{
    4.89 -	if (m_bCancelled)
    4.90 -	{
    4.91 -		/* If we were cancelled, don't bother tabulating anything.
    4.92 -		 * Reset just in case someone tries to redraw, which means
    4.93 -		 * we need to save our states.
    4.94 -		 */
    4.95 -		IBaseMenu *menu = m_pCurMenu;
    4.96 -		InternalReset();
    4.97 -		m_pHandler->OnMenuVoteCancel(menu);
    4.98 -		m_pHandler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
    4.99 -		return;
   4.100 -	}
   4.101 -
   4.102 -	unsigned int chosen = 0;
   4.103 -	unsigned int highest = 0;
   4.104 -	unsigned int dup_count = 0;
   4.105 -	unsigned int total = m_Votes.size() ? m_Votes[0] : 0;
   4.106 -
   4.107 -	/* If we got zero votes, take a shortcut. */
   4.108 -	if (m_NumVotes == 0)
   4.109 -	{
   4.110 -		/* Pick a random item and then jump far, far away. */
   4.111 -		srand((unsigned int)(time(NULL)));
   4.112 -		chosen = (unsigned int)rand() % m_Items;
   4.113 -		goto picked_item;
   4.114 -	}
   4.115 -
   4.116 -	/* We can't have more dups than this!
   4.117 -	 * This is the max number of players.
   4.118 -	 */
   4.119 -	unsigned int dup_array[256];
   4.120 -
   4.121 -	for (size_t i=1; i<m_Items; i++)
   4.122 -	{
   4.123 -		if (m_Votes[i] > m_Votes[highest])
   4.124 -		{
   4.125 -			/* If we have a new highest count, mark it and trash the duplicate
   4.126 -			 * list by setting the total to 0.
   4.127 -			 */
   4.128 -			highest = i;
   4.129 -			dup_count = 0;
   4.130 -		} else if (m_Votes[i] == m_Votes[highest]) {
   4.131 -			/* If they're equal, mark it in the duplicate list.
   4.132 -			 * We'll add in the original later.
   4.133 -			 */
   4.134 -			dup_array[dup_count++] = i;
   4.135 -		}
   4.136 -		total += m_Votes[i];
   4.137 -	}
   4.138 -
   4.139 -	/* Check if we need to pick from the duplicate list */
   4.140 -	if (dup_count)
   4.141 -	{
   4.142 -		/* Re-add the original to the list because it's not in there. */
   4.143 -		dup_array[dup_count++] = highest;
   4.144 -
   4.145 -		/* Pick a random slot. */
   4.146 -		srand((unsigned int)(time(NULL)));
   4.147 -		unsigned int r = (unsigned int)rand() % dup_count;
   4.148 -
   4.149 -		/* Pick the item. */
   4.150 -		chosen = dup_array[r];
   4.151 -	} else {
   4.152 -		chosen = highest;
   4.153 -	}
   4.154 -
   4.155 -picked_item:
   4.156 -	m_pHandler->OnMenuVoteEnd(m_pCurMenu, chosen, m_Votes[highest], total);
   4.157 -	m_pHandler->OnMenuEnd(m_pCurMenu, MenuEnd_VotingDone);
   4.158 -	InternalReset();
   4.159 -}
   4.160 -
   4.161 -void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
   4.162 -{
   4.163 -	m_Clients++;
   4.164 -}
   4.165 -
   4.166 -void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   4.167 -{
   4.168 -	DecrementPlayerCount();
   4.169 -}
   4.170 -
   4.171 -void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   4.172 -{
   4.173 -	m_pHandler->OnMenuCancel(menu, client, reason);
   4.174 -}
   4.175 -
   4.176 -void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
   4.177 -{
   4.178 -	m_pHandler->OnMenuDisplay(menu, client, display);
   4.179 -}
   4.180 -
   4.181 -unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
   4.182 -{
   4.183 -	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
   4.184 -}
   4.185 -
   4.186 -void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   4.187 -{
   4.188 -	m_pHandler->OnMenuDrawItem(menu, client, item, style);
   4.189 -}
   4.190 -
   4.191 -void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   4.192 -{
   4.193 -	/* Check by our item count, NOT the vote array size */
   4.194 -	if (item < m_Items)
   4.195 -	{
   4.196 -		m_Votes[item]++;
   4.197 -		m_NumVotes++;
   4.198 -	}
   4.199 -
   4.200 -	m_pHandler->OnMenuSelect(menu, client, item);
   4.201 -}
   4.202 -
   4.203 -void VoteMenuHandler::Reset(IMenuHandler *mh)
   4.204 -{
   4.205 -	m_pHandler = mh;
   4.206 -	InternalReset();
   4.207 -}
   4.208 -
   4.209 -void VoteMenuHandler::InternalReset()
   4.210 -{
   4.211 -	m_Clients = 0;
   4.212 -	m_Items = 0;
   4.213 -	m_bStarted = false;
   4.214 -	m_pCurMenu = NULL;
   4.215 -	m_NumVotes = 0;
   4.216 -	m_bCancelled = false;
   4.217 -}
   4.218 -
   4.219 -void VoteMenuHandler::CancelVoting()
   4.220 -{
   4.221 -	m_bCancelled = true;
   4.222 -}
   4.223 -
   4.224 -/*******************************
   4.225 - *******************************
   4.226 - ******** MENU MANAGER *********
   4.227 - *******************************
   4.228 - *******************************/
   4.229 -
   4.230  MenuManager::MenuManager()
   4.231  {
   4.232  	m_Styles.push_back(&g_ValveMenuStyle);
   4.233 @@ -283,12 +74,6 @@
   4.234  {
   4.235  	g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
   4.236  	g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
   4.237 -
   4.238 -	while (!m_VoteHandlers.empty())
   4.239 -	{
   4.240 -		delete m_VoteHandlers.front();
   4.241 -		m_VoteHandlers.pop();
   4.242 -	}
   4.243  }
   4.244  
   4.245  void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
   4.246 @@ -649,8 +434,8 @@
   4.247  	if (pgn != MENU_NO_PAGINATION)
   4.248  	{
   4.249  		bool canDrawDisabled = display->CanDrawItem(ITEMDRAW_DISABLED|ITEMDRAW_CONTROL);
   4.250 -		bool exitButton = menu->GetExitButton();
   4.251 -		bool exitBackButton = menu->GetExitBackButton();
   4.252 +		bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
   4.253 +		bool exitBackButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK;
   4.254  		char text[50];
   4.255  
   4.256  		/* Calculate how many items we are allowed for control stuff */
   4.257 @@ -798,33 +583,6 @@
   4.258  	return m_pDefaultStyle;
   4.259  }
   4.260  
   4.261 -IVoteMenuHandler *MenuManager::CreateVoteWrapper(IMenuHandler *mh)
   4.262 -{
   4.263 -	VoteMenuHandler *vh = NULL;
   4.264 -
   4.265 -	if (m_VoteHandlers.empty())
   4.266 -	{
   4.267 -		vh = new VoteMenuHandler;
   4.268 -	} else {
   4.269 -		vh = m_VoteHandlers.front();
   4.270 -		m_VoteHandlers.pop();
   4.271 -	}
   4.272 -
   4.273 -	vh->Reset(mh);
   4.274 -
   4.275 -	return vh;
   4.276 -}
   4.277 -
   4.278 -void MenuManager::ReleaseVoteWrapper(IVoteMenuHandler *mh)
   4.279 -{
   4.280 -	if (mh == NULL)
   4.281 -	{
   4.282 -		return;
   4.283 -	}
   4.284 -
   4.285 -	m_VoteHandlers.push((VoteMenuHandler *)mh);
   4.286 -}
   4.287 -
   4.288  bool MenuManager::MenuSoundsEnabled()
   4.289  {
   4.290  	return (sm_menu_sounds.GetInt() != 0);
   4.291 @@ -879,7 +637,7 @@
   4.292  		{
   4.293  			if (m_ExitSound.size() > 0)
   4.294  			{
   4.295 -				sound= m_ExitSound.c_str();
   4.296 +				sound = m_ExitSound.c_str();
   4.297  			}
   4.298  			break;
   4.299  		}
   4.300 @@ -907,3 +665,30 @@
   4.301  		enginesound->PrecacheSound(m_ExitSound.c_str(), true);
   4.302  	}
   4.303  }
   4.304 +
   4.305 +void MenuManager::CancelMenu(IBaseMenu *menu)
   4.306 +{
   4.307 +	if (s_VoteHandler.GetCurrentMenu() == menu
   4.308 +		&& !s_VoteHandler.IsCancelling())
   4.309 +	{
   4.310 +		s_VoteHandler.CancelVoting();
   4.311 +		return;
   4.312 +	}
   4.313 +
   4.314 +	menu->Cancel();
   4.315 +}
   4.316 +
   4.317 +bool MenuManager::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags)
   4.318 +{
   4.319 +	return s_VoteHandler.StartVote(menu, num_clients, clients, max_time, flags);
   4.320 +}
   4.321 +
   4.322 +bool MenuManager::IsVoteInProgress()
   4.323 +{
   4.324 +	return s_VoteHandler.IsVoteInProgress();
   4.325 +}
   4.326 +
   4.327 +void MenuManager::CancelVoting()
   4.328 +{
   4.329 +	s_VoteHandler.CancelVoting();
   4.330 +}
     5.1 --- a/core/MenuManager.h	Tue Aug 07 22:11:00 2007 +0000
     5.2 +++ b/core/MenuManager.h	Wed Aug 08 02:27:10 2007 +0000
     5.3 @@ -44,39 +44,6 @@
     5.4  using namespace SourceMod;
     5.5  using namespace SourceHook;
     5.6  
     5.7 -class VoteMenuHandler : public IVoteMenuHandler
     5.8 -{
     5.9 -public: //IMenuHandler
    5.10 -	unsigned int GetMenuAPIVersion2();
    5.11 -	void OnMenuStart(IBaseMenu *menu);
    5.12 -	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
    5.13 -	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    5.14 -	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    5.15 -	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
    5.16 -	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
    5.17 -	unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
    5.18 -public: //IVoteMenuHandler
    5.19 -	bool IsVoteInProgress();
    5.20 -	void InitializeVoting(IBaseMenu *menu);
    5.21 -	void StartVoting();
    5.22 -	void CancelVoting();
    5.23 -public:
    5.24 -	void Reset(IMenuHandler *mh);
    5.25 -private:
    5.26 -	void DecrementPlayerCount();
    5.27 -	void EndVoting();
    5.28 -	void InternalReset();
    5.29 -private:
    5.30 -	IMenuHandler *m_pHandler;
    5.31 -	unsigned int m_Clients;
    5.32 -	unsigned int m_Items;
    5.33 -	CVector<unsigned int> m_Votes;
    5.34 -	IBaseMenu *m_pCurMenu;
    5.35 -	bool m_bStarted;
    5.36 -	bool m_bCancelled;
    5.37 -	unsigned int m_NumVotes;
    5.38 -};
    5.39 -
    5.40  class MenuManager : 
    5.41  	public IMenuManager,
    5.42  	public SMGlobalClass,
    5.43 @@ -114,8 +81,14 @@
    5.44  	void AddStyle(IMenuStyle *style);
    5.45  	bool SetDefaultStyle(IMenuStyle *style);
    5.46  	IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order);
    5.47 -	IVoteMenuHandler *CreateVoteWrapper(IMenuHandler *mh);
    5.48 -	void ReleaseVoteWrapper(IVoteMenuHandler *mh);
    5.49 +	void CancelMenu(IBaseMenu *menu);
    5.50 +	bool StartVote(IBaseMenu *menu,
    5.51 +		unsigned int num_clients,
    5.52 +		int clients[],
    5.53 +		unsigned int max_time,
    5.54 +		unsigned int flags=0);
    5.55 +	bool IsVoteInProgress();
    5.56 +	void CancelVoting();
    5.57  public: //IHandleTypeDispatch
    5.58  	void OnHandleDestroy(HandleType_t type, void *object);
    5.59  public:
    5.60 @@ -130,7 +103,6 @@
    5.61  private:
    5.62  	int m_ShowMenu;
    5.63  	IMenuStyle *m_pDefaultStyle;
    5.64 -	CStack<VoteMenuHandler *> m_VoteHandlers;
    5.65  	CVector<IMenuStyle *> m_Styles;
    5.66  	HandleType_t m_StyleType;
    5.67  	HandleType_t m_MenuType;
     6.1 --- a/core/MenuStyle_Base.cpp	Tue Aug 07 22:11:00 2007 +0000
     6.2 +++ b/core/MenuStyle_Base.cpp	Wed Aug 08 02:27:10 2007 +0000
     6.3 @@ -584,14 +584,12 @@
     6.4  CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : 
     6.5  m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), 
     6.6  m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), 
     6.7 -m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_pVoteHandler(NULL), 
     6.8 -m_nFlags(MENUFLAG_BUTTON_EXIT)
     6.9 +m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
    6.10  {
    6.11  }
    6.12  
    6.13  CBaseMenu::~CBaseMenu()
    6.14  {
    6.15 -	g_Menus.ReleaseVoteWrapper(m_pVoteHandler);
    6.16  }
    6.17  
    6.18  Handle_t CBaseMenu::GetHandle()
    6.19 @@ -732,22 +730,6 @@
    6.20  	return m_Title.c_str();
    6.21  }
    6.22  
    6.23 -bool CBaseMenu::GetExitButton()
    6.24 -{
    6.25 -	return ((m_nFlags & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT);
    6.26 -}
    6.27 -
    6.28 -bool CBaseMenu::SetExitButton(bool set)
    6.29 -{
    6.30 -	if (set)
    6.31 -	{
    6.32 -		m_nFlags |= MENUFLAG_BUTTON_EXIT;
    6.33 -	} else {
    6.34 -		m_nFlags &= ~MENUFLAG_BUTTON_EXIT;
    6.35 -	}
    6.36 -	return true;
    6.37 -}
    6.38 -
    6.39  void CBaseMenu::Cancel()
    6.40  {
    6.41  	if (m_bCancelling)
    6.42 @@ -756,18 +738,11 @@
    6.43  	}
    6.44  
    6.45  #if defined MENU_DEBUG
    6.46 -	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_pVote %p) (inVote %d) (m_bShouldDelete %d)",
    6.47 +	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
    6.48  		this,
    6.49 -		m_pVoteHandler,
    6.50 -		m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false,
    6.51  		m_bShouldDelete);
    6.52  #endif
    6.53  
    6.54 -	if (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress())
    6.55 -	{
    6.56 -		m_pVoteHandler->CancelVoting();
    6.57 -	}
    6.58 -
    6.59  	m_bCancelling = true;
    6.60  	Cancel_Finally();
    6.61  	m_bCancelling = false;
    6.62 @@ -827,58 +802,6 @@
    6.63  	delete this;
    6.64  }
    6.65  
    6.66 -bool CBaseMenu::BroadcastVote(int clients[], 
    6.67 -							  unsigned int numClients, 
    6.68 -							  unsigned int maxTime,
    6.69 -							  unsigned int flags)
    6.70 -{
    6.71 -#if defined MENU_DEBUG
    6.72 -	g_Logger.LogMessage("[SM_MENU] CBaseMenu::BroadcastVote(%p) (maxTime %d) (numClients %d) (m_pVote %p) (inVote %d)",
    6.73 -		this,
    6.74 -		maxTime,
    6.75 -		numClients,
    6.76 -		m_pVoteHandler,
    6.77 -		m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false);
    6.78 -#endif
    6.79 -	if (!m_pVoteHandler)
    6.80 -	{
    6.81 -		m_pVoteHandler = g_Menus.CreateVoteWrapper(m_pHandler);
    6.82 -	} else if (m_pVoteHandler->IsVoteInProgress()) {
    6.83 -		return false;
    6.84 -	}
    6.85 -
    6.86 -	m_pVoteHandler->InitializeVoting(this);
    6.87 -
    6.88 -	for (unsigned int i=0; i<numClients; i++)
    6.89 -	{
    6.90 -		VoteDisplay(clients[i], maxTime);
    6.91 -	}
    6.92 -
    6.93 -	m_pVoteHandler->StartVoting();
    6.94 -
    6.95 -	return true;
    6.96 -}
    6.97 -
    6.98 -bool CBaseMenu::IsVoteInProgress()
    6.99 -{
   6.100 -	return (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress());
   6.101 -}
   6.102 -
   6.103 -bool CBaseMenu::GetExitBackButton()
   6.104 -{
   6.105 -	return ((m_nFlags & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK);
   6.106 -}
   6.107 -
   6.108 -void CBaseMenu::SetExitBackButton(bool set)
   6.109 -{
   6.110 -	if (set)
   6.111 -	{
   6.112 -		m_nFlags |= MENUFLAG_BUTTON_EXITBACK;
   6.113 -	} else {
   6.114 -		m_nFlags &= ~MENUFLAG_BUTTON_EXITBACK;
   6.115 -	}
   6.116 -}
   6.117 -
   6.118  unsigned int CBaseMenu::GetMenuOptionFlags()
   6.119  {
   6.120  	return m_nFlags;
   6.121 @@ -888,3 +811,8 @@
   6.122  {
   6.123  	m_nFlags = flags;
   6.124  }
   6.125 +
   6.126 +IMenuHandler *CBaseMenu::GetHandler()
   6.127 +{
   6.128 +	return m_pHandler;
   6.129 +}
     7.1 --- a/core/MenuStyle_Base.h	Tue Aug 07 22:11:00 2007 +0000
     7.2 +++ b/core/MenuStyle_Base.h	Wed Aug 08 02:27:10 2007 +0000
     7.3 @@ -125,23 +125,13 @@
     7.4  	virtual IMenuStyle *GetDrawStyle();
     7.5  	virtual void SetDefaultTitle(const char *message);
     7.6  	virtual const char *GetDefaultTitle();
     7.7 -	virtual bool GetExitButton();
     7.8 -	virtual bool SetExitButton(bool set);
     7.9  	virtual void Cancel();
    7.10  	virtual void Destroy(bool releaseHandle);
    7.11  	virtual void Cancel_Finally() =0;
    7.12  	virtual Handle_t GetHandle();
    7.13 -	virtual bool BroadcastVote(int clients[], 
    7.14 -		unsigned int numClients, 
    7.15 -		unsigned int maxTime,
    7.16 -		unsigned int flags=0);
    7.17 -	virtual bool IsVoteInProgress();
    7.18 -	virtual bool GetExitBackButton();
    7.19 -	virtual void SetExitBackButton(bool set);
    7.20  	virtual unsigned int GetMenuOptionFlags();
    7.21  	virtual void SetMenuOptionFlags(unsigned int flags);
    7.22 -public:
    7.23 -	virtual void VoteDisplay(int client, unsigned int maxTime) =0;
    7.24 +	virtual IMenuHandler *GetHandler();
    7.25  private:
    7.26  	void InternalDelete();
    7.27  protected:
    7.28 @@ -157,7 +147,6 @@
    7.29  	bool m_bWillFreeHandle;
    7.30  	Handle_t m_hHandle;
    7.31  	IMenuHandler *m_pHandler;
    7.32 -	IVoteMenuHandler *m_pVoteHandler;
    7.33  	unsigned int m_nFlags;
    7.34  };
    7.35  
     8.1 --- a/core/MenuStyle_Radio.cpp	Tue Aug 07 22:11:00 2007 +0000
     8.2 +++ b/core/MenuStyle_Radio.cpp	Wed Aug 08 02:27:10 2007 +0000
     8.3 @@ -390,7 +390,7 @@
     8.4  	return g_RadioMenuStyle.MakeRadioDisplay(this);
     8.5  }
     8.6  
     8.7 -bool CRadioMenu::Display(int client, unsigned int time)
     8.8 +bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
     8.9  {
    8.10  #if defined MENU_DEBUG
    8.11  	g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
    8.12 @@ -403,23 +403,7 @@
    8.13  		return false;
    8.14  	}
    8.15  
    8.16 -	return g_RadioMenuStyle.DoClientMenu(client, this, m_pHandler, time);
    8.17 -}
    8.18 -
    8.19 -void CRadioMenu::VoteDisplay(int client, unsigned int maxTime)
    8.20 -{
    8.21 -#if defined MENU_DEBUG
    8.22 -	g_Logger.LogMessage("[SM_MENU] CRadioMenu::VoteDisplay(%p) (client %d) (time %d)",
    8.23 -		this,
    8.24 -		client,
    8.25 -		maxTime);
    8.26 -#endif
    8.27 -	if (m_bCancelling)
    8.28 -	{
    8.29 -		return;
    8.30 -	}
    8.31 -
    8.32 -	g_RadioMenuStyle.DoClientMenu(client, this, m_pVoteHandler, maxTime);
    8.33 +	return g_RadioMenuStyle.DoClientMenu(client, this, alt_handler ? alt_handler : m_pHandler, time);
    8.34  }
    8.35  
    8.36  void CRadioMenu::Cancel_Finally()
     9.1 --- a/core/MenuStyle_Radio.h	Tue Aug 07 22:11:00 2007 +0000
     9.2 +++ b/core/MenuStyle_Radio.h	Wed Aug 08 02:27:10 2007 +0000
     9.3 @@ -114,9 +114,8 @@
     9.4  public:
     9.5  	bool SetExtOption(MenuOption option, const void *valuePtr);
     9.6  	IMenuPanel *CreatePanel();
     9.7 -	bool Display(int client, unsigned int time);
     9.8 +	bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
     9.9  	void Cancel_Finally();
    9.10 -	void VoteDisplay(int client, unsigned int maxTime);
    9.11  };
    9.12  
    9.13  extern CRadioStyle g_RadioMenuStyle;
    10.1 --- a/core/MenuStyle_Valve.cpp	Tue Aug 07 22:11:00 2007 +0000
    10.2 +++ b/core/MenuStyle_Valve.cpp	Wed Aug 08 02:27:10 2007 +0000
    10.3 @@ -383,24 +383,14 @@
    10.4  	return false;
    10.5  }
    10.6  
    10.7 -bool CValveMenu::Display(int client, unsigned int time)
    10.8 +bool CValveMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
    10.9  {
   10.10  	if (m_bCancelling)
   10.11  	{
   10.12  		return false;
   10.13  	}
   10.14  
   10.15 -	return g_ValveMenuStyle.DoClientMenu(client, this, m_pHandler, time);
   10.16 -}
   10.17 -
   10.18 -void CValveMenu::VoteDisplay(int client, unsigned int maxTime)
   10.19 -{
   10.20 -	if (m_bCancelling)
   10.21 -	{
   10.22 -		return;
   10.23 -	}
   10.24 -
   10.25 -	g_ValveMenuStyle.DoClientMenu(client, this, m_pVoteHandler, maxTime);
   10.26 +	return g_ValveMenuStyle.DoClientMenu(client, this, alt_handler ? alt_handler : m_pHandler, time);
   10.27  }
   10.28  
   10.29  IMenuPanel *CValveMenu::CreatePanel()
   10.30 @@ -408,19 +398,9 @@
   10.31  	return new CValveMenuDisplay(this);
   10.32  }
   10.33  
   10.34 -bool CValveMenu::GetExitButton()
   10.35 -{
   10.36 -	return true;
   10.37 -}
   10.38 -
   10.39 -bool CValveMenu::SetExitButton(bool set)
   10.40 -{
   10.41 -	return false;
   10.42 -}
   10.43 -
   10.44  void CValveMenu::SetMenuOptionFlags(unsigned int flags)
   10.45  {
   10.46 -	flags &= ~MENUFLAG_BUTTON_EXIT;
   10.47 +	flags |= MENUFLAG_BUTTON_EXIT;
   10.48  	CBaseMenu::SetMenuOptionFlags(flags);
   10.49  }
   10.50  
    11.1 --- a/core/MenuStyle_Valve.h	Tue Aug 07 22:11:00 2007 +0000
    11.2 +++ b/core/MenuStyle_Valve.h	Wed Aug 08 02:27:10 2007 +0000
    11.3 @@ -120,8 +120,7 @@
    11.4  	bool GetExitButton();
    11.5  	bool SetExitButton(bool set);
    11.6  	bool SetPagination(unsigned int itemsPerPage);
    11.7 -	bool Display(int client, unsigned int time);
    11.8 -	void VoteDisplay(int client, unsigned int maxTime);
    11.9 +	bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
   11.10  	void SetMenuOptionFlags(unsigned int flags);
   11.11  public: //CBaseMenu
   11.12  	void Cancel_Finally();
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/core/MenuVoting.cpp	Wed Aug 08 02:27:10 2007 +0000
    12.3 @@ -0,0 +1,326 @@
    12.4 +/**
    12.5 + * vim: set ts=4 :
    12.6 + * ================================================================
    12.7 + * SourceMod
    12.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
    12.9 + * ================================================================
   12.10 + *
   12.11 + * This program is free software; you can redistribute it and/or 
   12.12 + * modify it under the terms of the GNU General Public License, 
   12.13 + * version 3.0, as published by the Free Software Foundation.
   12.14 + * 
   12.15 + * This program is distributed in the hope that it will be useful,
   12.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.18 + * GNU General Public License for more details.
   12.19 + *
   12.20 + * You should have received a copy of the GNU General Public License
   12.21 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   12.22 + *
   12.23 + * As a special exception, AlliedModders LLC gives you permission to 
   12.24 + * link the code of this program (as well as its derivative works) to 
   12.25 + * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
   12.26 + * Game MODs that run on software by the Valve Corporation.  You must 
   12.27 + * obey the GNU General Public License in all respects for all other 
   12.28 + * code used. Additionally, AlliedModders LLC grants this exception 
   12.29 + * to all derivative works. AlliedModders LLC defines further 
   12.30 + * exceptions, found in LICENSE.txt (as of this writing, version 
   12.31 + * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
   12.32 + *
   12.33 + * Version: $Id$
   12.34 + */
   12.35 +
   12.36 +#include <string.h>
   12.37 +#include <stdlib.h>
   12.38 +#include "MenuVoting.h"
   12.39 +#include "PlayerManager.h"
   12.40 +#include "sourcemm_api.h"
   12.41 +
   12.42 +void VoteMenuHandler::OnSourceModAllInitialized()
   12.43 +{
   12.44 +	g_Players.AddClientListener(this);
   12.45 +}
   12.46 +
   12.47 +void VoteMenuHandler::OnSourceModShutdown()
   12.48 +{
   12.49 +	g_Players.RemoveClientListener(this);
   12.50 +}
   12.51 +
   12.52 +unsigned int VoteMenuHandler::GetMenuAPIVersion2()
   12.53 +{
   12.54 +	return m_pHandler->GetMenuAPIVersion2();
   12.55 +}
   12.56 +
   12.57 +void VoteMenuHandler::OnClientDisconnected(int client)
   12.58 +{
   12.59 +	if (!IsVoteInProgress())
   12.60 +	{
   12.61 +		return;
   12.62 +	}
   12.63 +
   12.64 +	/* Wipe out their vote if they had one */
   12.65 +	int item;
   12.66 +	if ((item = m_ClientVotes[client]) >= 0)
   12.67 +	{
   12.68 +		assert((unsigned)item < m_Items);
   12.69 +		assert(m_Votes[item] > 0);
   12.70 +		m_Votes[item]--;
   12.71 +		m_ClientVotes[client] = -1;
   12.72 +	}
   12.73 +}
   12.74 +
   12.75 +bool VoteMenuHandler::IsVoteInProgress()
   12.76 +{
   12.77 +	return (m_pCurMenu != NULL);
   12.78 +}
   12.79 +
   12.80 +bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
   12.81 +{
   12.82 +	if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
   12.83 +	{
   12.84 +		return false;
   12.85 +	}
   12.86 +
   12.87 +	for (unsigned int i=0; i<num_clients; i++)
   12.88 +	{
   12.89 +		menu->Display(clients[i], max_time, this);
   12.90 +	}
   12.91 +
   12.92 +	StartVoting();
   12.93 +
   12.94 +	return true;
   12.95 +}
   12.96 +
   12.97 +bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, 
   12.98 +									   IMenuHandler *handler,
   12.99 +									   unsigned int time,
  12.100 +									   unsigned int flags)
  12.101 +{
  12.102 +	if (IsVoteInProgress())
  12.103 +	{
  12.104 +		return false;
  12.105 +	}
  12.106 +
  12.107 +	InternalReset();
  12.108 +
  12.109 +	/* Mark all clients as not voting */
  12.110 +	for (int i=1; i<=gpGlobals->maxClients; i++)
  12.111 +	{
  12.112 +		m_ClientVotes[i] = -2;
  12.113 +	}
  12.114 +
  12.115 +	m_Items = menu->GetItemCount();
  12.116 +
  12.117 +	if (m_Votes.size() < (size_t)m_Items)
  12.118 +	{
  12.119 +		/* Only clear the items we need to... */
  12.120 +		size_t size = m_Votes.size();
  12.121 +		for (size_t i=0; i<size; i++)
  12.122 +		{
  12.123 +			m_Votes[i] = 0;
  12.124 +		}
  12.125 +		m_Votes.resize(m_Items, 0);
  12.126 +	} else {
  12.127 +		for (unsigned int i=0; i<m_Items; i++)
  12.128 +		{
  12.129 +			m_Votes[i] = 0;
  12.130 +		}
  12.131 +	}
  12.132 +
  12.133 +	m_pCurMenu = menu;
  12.134 +	m_VoteTime = time;
  12.135 +	m_VoteFlags = flags;
  12.136 +	m_pHandler = handler;
  12.137 +
  12.138 +	m_pHandler->OnMenuStart(m_pCurMenu);
  12.139 +
  12.140 +	return true;
  12.141 +}
  12.142 +
  12.143 +void VoteMenuHandler::StartVoting()
  12.144 +{
  12.145 +	if (!m_pCurMenu)
  12.146 +	{
  12.147 +		return;
  12.148 +	}
  12.149 +
  12.150 +	m_bStarted = true;
  12.151 +
  12.152 +	m_pHandler->OnMenuVoteStart(m_pCurMenu);
  12.153 +
  12.154 +	/* By now we know how many clients were set.  
  12.155 +	 * If there are none, we should end IMMEDIATELY.
  12.156 +	 */
  12.157 +	if (m_Clients == 0)
  12.158 +	{
  12.159 +		EndVoting();
  12.160 +	}
  12.161 +}
  12.162 +
  12.163 +void VoteMenuHandler::DecrementPlayerCount()
  12.164 +{
  12.165 +	assert(m_Clients > 0);
  12.166 +
  12.167 +	m_Clients--;
  12.168 +
  12.169 +	if (m_bStarted && m_Clients == 0)
  12.170 +	{
  12.171 +		EndVoting();
  12.172 +	}
  12.173 +}
  12.174 +
  12.175 +int SortVoteItems(const void *item1, const void *item2)
  12.176 +{
  12.177 +	return ((menu_vote_result_t::menu_item_vote_t *)item2)->count 
  12.178 +		- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
  12.179 +}
  12.180 +
  12.181 +void VoteMenuHandler::EndVoting()
  12.182 +{
  12.183 +	if (m_bCancelled)
  12.184 +	{
  12.185 +		/* If we were cancelled, don't bother tabulating anything.
  12.186 +		 * Reset just in case someone tries to redraw, which means
  12.187 +		 * we need to save our states.
  12.188 +		 */
  12.189 +		IBaseMenu *menu = m_pCurMenu;
  12.190 +		IMenuHandler *handler = m_pHandler;
  12.191 +		InternalReset();
  12.192 +		handler->OnMenuVoteCancel(menu);
  12.193 +		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
  12.194 +		return;
  12.195 +	}
  12.196 +
  12.197 +	menu_vote_result_t vote;
  12.198 +	menu_vote_result_t::menu_client_vote_t client_vote[256];
  12.199 +	menu_vote_result_t::menu_item_vote_t item_vote[256];
  12.200 +
  12.201 +	memset(&vote, 0, sizeof(vote));
  12.202 +
  12.203 +	/* Build the item list */
  12.204 +	for (unsigned int i=0; i<m_Items; i++)
  12.205 +	{
  12.206 +		if (m_Votes[i] > 0)
  12.207 +		{
  12.208 +			item_vote[vote.num_items].count = m_Votes[i];
  12.209 +			item_vote[vote.num_items].item = i;
  12.210 +			vote.num_votes += m_Votes[i];
  12.211 +			vote.num_items++;
  12.212 +		}
  12.213 +	}
  12.214 +	vote.item_list = item_vote;
  12.215 +
  12.216 +	if (!vote.num_votes)
  12.217 +	{
  12.218 +		IBaseMenu *menu = m_pCurMenu;
  12.219 +		IMenuHandler *handler = m_pHandler;
  12.220 +		InternalReset();
  12.221 +		handler->OnMenuVoteCancel(menu);
  12.222 +		handler->OnMenuEnd(menu, MenuEnd_NoVotes);
  12.223 +		return;
  12.224 +	}
  12.225 +
  12.226 +	/* Build the client list */
  12.227 +	for (int i=1; i<=gpGlobals->maxClients; i++)
  12.228 +	{
  12.229 +		if (m_ClientVotes[i] >= -1)
  12.230 +		{
  12.231 +			client_vote[vote.num_clients].client = i;
  12.232 +			client_vote[vote.num_clients].item = m_ClientVotes[i];
  12.233 +			vote.num_clients++;
  12.234 +		}
  12.235 +	}
  12.236 +	vote.client_list = client_vote;
  12.237 +
  12.238 +	/* Sort the item list descending like we promised */
  12.239 +	qsort(item_vote,
  12.240 +		vote.num_items,
  12.241 +		sizeof(menu_vote_result_t::menu_item_vote_t),
  12.242 +		SortVoteItems);
  12.243 +
  12.244 +	/* Save states, then clear what we've saved.
  12.245 +	 * This makes us re-entrant, which is always the safe way to go.
  12.246 +	 */
  12.247 +	IBaseMenu *menu = m_pCurMenu;
  12.248 +	IMenuHandler *handler = m_pHandler;
  12.249 +	InternalReset();
  12.250 +
  12.251 +	/* Send vote info */
  12.252 +	handler->OnMenuVoteResults(menu, &vote);
  12.253 +	handler->OnMenuEnd(menu, MenuEnd_VotingDone);
  12.254 +}
  12.255 +
  12.256 +void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
  12.257 +{
  12.258 +	m_Clients++;
  12.259 +}
  12.260 +
  12.261 +void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
  12.262 +{
  12.263 +	DecrementPlayerCount();
  12.264 +}
  12.265 +
  12.266 +void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
  12.267 +{
  12.268 +	m_pHandler->OnMenuCancel(menu, client, reason);
  12.269 +}
  12.270 +
  12.271 +void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
  12.272 +{
  12.273 +	m_ClientVotes[client] = -1;
  12.274 +	m_pHandler->OnMenuDisplay(menu, client, display);
  12.275 +}
  12.276 +
  12.277 +unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
  12.278 +{
  12.279 +	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
  12.280 +}
  12.281 +
  12.282 +void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
  12.283 +{
  12.284 +	m_pHandler->OnMenuDrawItem(menu, client, item, style);
  12.285 +}
  12.286 +
  12.287 +void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
  12.288 +{
  12.289 +	/* Check by our item count, NOT the vote array size */
  12.290 +	if (item < m_Items)
  12.291 +	{
  12.292 +		m_ClientVotes[client] = item;
  12.293 +		m_Votes[item]++;
  12.294 +		m_NumVotes++;
  12.295 +	}
  12.296 +
  12.297 +	m_pHandler->OnMenuSelect(menu, client, item);
  12.298 +}
  12.299 +
  12.300 +void VoteMenuHandler::InternalReset()
  12.301 +{
  12.302 +	m_Clients = 0;
  12.303 +	m_Items = 0;
  12.304 +	m_bStarted = false;
  12.305 +	m_pCurMenu = NULL;
  12.306 +	m_NumVotes = 0;
  12.307 +	m_bCancelled = false;
  12.308 +	m_pHandler = NULL;
  12.309 +}
  12.310 +
  12.311 +void VoteMenuHandler::CancelVoting()
  12.312 +{
  12.313 +	if (m_bCancelled || !m_pCurMenu)
  12.314 +	{
  12.315 +		return;
  12.316 +	}
  12.317 +	m_bCancelled = true;
  12.318 +	m_pCurMenu->Cancel();
  12.319 +}
  12.320 +
  12.321 +IBaseMenu *VoteMenuHandler::GetCurrentMenu()
  12.322 +{
  12.323 +	return m_pCurMenu;
  12.324 +}
  12.325 +
  12.326 +bool VoteMenuHandler::IsCancelling()
  12.327 +{
  12.328 +	return m_bCancelled;
  12.329 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/core/MenuVoting.h	Wed Aug 08 02:27:10 2007 +0000
    13.3 @@ -0,0 +1,97 @@
    13.4 +/**
    13.5 + * vim: set ts=4 :
    13.6 + * ================================================================
    13.7 + * SourceMod
    13.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
    13.9 + * ================================================================
   13.10 + *
   13.11 + * This program is free software; you can redistribute it and/or 
   13.12 + * modify it under the terms of the GNU General Public License, 
   13.13 + * version 3.0, as published by the Free Software Foundation.
   13.14 + * 
   13.15 + * This program is distributed in the hope that it will be useful,
   13.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.18 + * GNU General Public License for more details.
   13.19 + *
   13.20 + * You should have received a copy of the GNU General Public License
   13.21 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   13.22 + *
   13.23 + * As a special exception, AlliedModders LLC gives you permission to 
   13.24 + * link the code of this program (as well as its derivative works) to 
   13.25 + * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
   13.26 + * Game MODs that run on software by the Valve Corporation.  You must 
   13.27 + * obey the GNU General Public License in all respects for all other 
   13.28 + * code used. Additionally, AlliedModders LLC grants this exception 
   13.29 + * to all derivative works. AlliedModders LLC defines further 
   13.30 + * exceptions, found in LICENSE.txt (as of this writing, version 
   13.31 + * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
   13.32 + *
   13.33 + * Version: $Id$
   13.34 + */
   13.35 +
   13.36 +#ifndef _INCLUDE_SOURCEMOD_MENUVOTING_H_
   13.37 +#define _INCLUDE_SOURCEMOD_MENUVOTING_H_
   13.38 +
   13.39 +#include <IMenuManager.h>
   13.40 +#include <IPlayerHelpers.h>
   13.41 +#include <sh_vector.h>
   13.42 +#include "sm_globals.h"
   13.43 +
   13.44 +using namespace SourceHook;
   13.45 +using namespace SourceMod;
   13.46 +
   13.47 +class VoteMenuHandler : 
   13.48 +	public IMenuHandler, 
   13.49 +	public SMGlobalClass,
   13.50 +	public IClientListener
   13.51 +{
   13.52 +public: //SMGlobalClass
   13.53 +	void OnSourceModAllInitialized();
   13.54 +	void OnSourceModShutdown();
   13.55 +public: //IClientListener
   13.56 +	void OnClientDisconnected(int client);
   13.57 +public: //IMenuHandler
   13.58 +	unsigned int GetMenuAPIVersion2();
   13.59 +	void OnMenuStart(IBaseMenu *menu);
   13.60 +	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
   13.61 +	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
   13.62 +	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
   13.63 +	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
   13.64 +	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   13.65 +	unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
   13.66 +public:
   13.67 +	bool StartVote(IBaseMenu *menu,
   13.68 +		unsigned int num_clients,
   13.69 +		int clients[],
   13.70 +		unsigned int max_time,
   13.71 +		unsigned int flags=0);
   13.72 +	bool IsVoteInProgress();
   13.73 +	void CancelVoting();
   13.74 +	IBaseMenu *GetCurrentMenu();
   13.75 +	bool IsCancelling();
   13.76 +private:
   13.77 +	void Reset(IMenuHandler *mh);
   13.78 +	void DecrementPlayerCount();
   13.79 +	void EndVoting();
   13.80 +	void InternalReset();
   13.81 +	bool InitializeVoting(IBaseMenu *menu, 
   13.82 +		IMenuHandler *handler,
   13.83 +		unsigned int time,
   13.84 +		unsigned int flags);
   13.85 +	void StartVoting();
   13.86 +private:
   13.87 +	IMenuHandler *m_pHandler;
   13.88 +	unsigned int m_Clients;
   13.89 +	unsigned int m_Items;
   13.90 +	CVector<unsigned int> m_Votes;
   13.91 +	IBaseMenu *m_pCurMenu;
   13.92 +	bool m_bStarted;
   13.93 +	bool m_bCancelled;
   13.94 +	unsigned int m_NumVotes;
   13.95 +	unsigned int m_VoteTime;
   13.96 +	unsigned int m_VoteFlags;
   13.97 +	int m_ClientVotes[256+1];
   13.98 +};
   13.99 +
  13.100 +#endif //_INCLUDE_SOURCEMOD_MENUVOTING_H_
    14.1 --- a/core/msvc8/sourcemod_mm.vcproj	Tue Aug 07 22:11:00 2007 +0000
    14.2 +++ b/core/msvc8/sourcemod_mm.vcproj	Wed Aug 08 02:27:10 2007 +0000
    14.3 @@ -337,6 +337,10 @@
    14.4  				>
    14.5  			</File>
    14.6  			<File
    14.7 +				RelativePath="..\MenuVoting.cpp"
    14.8 +				>
    14.9 +			</File>
   14.10 +			<File
   14.11  				RelativePath="..\PlayerManager.cpp"
   14.12  				>
   14.13  			</File>
   14.14 @@ -475,6 +479,10 @@
   14.15  				>
   14.16  			</File>
   14.17  			<File
   14.18 +				RelativePath="..\MenuVoting.h"
   14.19 +				>
   14.20 +			</File>
   14.21 +			<File
   14.22  				RelativePath="..\PlayerManager.h"
   14.23  				>
   14.24  			</File>
    15.1 --- a/core/smn_menus.cpp	Tue Aug 07 22:11:00 2007 +0000
    15.2 +++ b/core/smn_menus.cpp	Wed Aug 08 02:27:10 2007 +0000
    15.3 @@ -32,6 +32,7 @@
    15.4  
    15.5  #include "sm_globals.h"
    15.6  #include <sh_stack.h>
    15.7 +#include "DebugReporter.h"
    15.8  #include "MenuManager.h"
    15.9  #include "MenuStyle_Valve.h"
   15.10  #include "MenuStyle_Radio.h"
   15.11 @@ -93,13 +94,11 @@
   15.12  	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
   15.13  	void OnMenuDestroy(IBaseMenu *menu);
   15.14  	void OnMenuVoteStart(IBaseMenu *menu);
   15.15 -	void OnMenuVoteEnd(IBaseMenu *menu,
   15.16 -		unsigned int item, 
   15.17 -		unsigned int winningVotes,
   15.18 -		unsigned int totalVotes);
   15.19 +	void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
   15.20  	void OnMenuVoteCancel(IBaseMenu *menu);
   15.21  	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   15.22  	unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
   15.23 +	bool OnSetHandlerOption(const char *option, const void *data);
   15.24  #if 0
   15.25  	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   15.26  	void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
   15.27 @@ -109,6 +108,8 @@
   15.28  private:
   15.29  	IPluginFunction *m_pBasic;
   15.30  	int m_Flags;
   15.31 +	IPluginFunction *m_pVoteResults;
   15.32 +	cell_t m_fnVoteResult;
   15.33  };
   15.34  
   15.35  /**
   15.36 @@ -158,7 +159,7 @@
   15.37  	}
   15.38  
   15.39  	/**
   15.40 -	 *   It is extremely important that unloaded plugins don't crash.
   15.41 +	 * It is extremely important that unloaded plugins don't crash.
   15.42  	 * Thus, if a plugin unloads, we run through every handler we have.
   15.43  	 * This means we do almost no runtime work for keeping track of
   15.44  	 * our panel handlers (we don't have to store a list of the running 
   15.45 @@ -221,6 +222,7 @@
   15.46  			m_FreeMenuHandlers.pop();
   15.47  			handler->m_pBasic = pFunction;
   15.48  			handler->m_Flags = flags;
   15.49 +			handler->m_pVoteResults = NULL;
   15.50  		}
   15.51  		return handler;
   15.52  	}
   15.53 @@ -276,7 +278,7 @@
   15.54   * MENU HANDLER WRAPPER
   15.55   */
   15.56  CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   15.57 -	m_pBasic(pBasic), m_Flags(flags)
   15.58 +	m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
   15.59  {
   15.60  	/* :TODO: We can probably cache the handle ahead of time */
   15.61  }
   15.62 @@ -334,14 +336,6 @@
   15.63  	DoAction(menu, MenuAction_VoteStart, 0, 0);
   15.64  }
   15.65  
   15.66 -void CMenuHandler::OnMenuVoteEnd(IBaseMenu *menu,
   15.67 -								 unsigned int item,
   15.68 -								 unsigned int winningVotes,
   15.69 -								 unsigned int totalVotes)
   15.70 -{
   15.71 -	DoAction(menu, MenuAction_VoteEnd, item, (totalVotes << 16) | (winningVotes & 0xFFFF));
   15.72 -}
   15.73 -
   15.74  void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu)
   15.75  {
   15.76  	DoAction(menu, MenuAction_VoteCancel, 0, 0);
   15.77 @@ -407,6 +401,145 @@
   15.78  	return res;
   15.79  }
   15.80  
   15.81 +void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
   15.82 +{
   15.83 +	if (!m_pVoteResults)
   15.84 +	{
   15.85 +		/* Call MenuAction_VoteEnd instead.  See if there are any extra winners. */
   15.86 +		unsigned int num_items = 1;
   15.87 +		for (unsigned int i=1; i<results->num_items; i++)
   15.88 +		{
   15.89 +			if (results->item_list[i].count != results->item_list[0].count)
   15.90 +			{
   15.91 +				break;
   15.92 +			}
   15.93 +			num_items++;
   15.94 +		}
   15.95 +
   15.96 +		/* See if we need to pick a random winner. */
   15.97 +		unsigned int winning_item;
   15.98 +		if (num_items > 1)
   15.99 +		{
  15.100 +			/* Yes, we do. */
  15.101 +			srand(time(NULL));
  15.102 +			winning_item = rand() % num_items;
  15.103 +			winning_item = results->item_list[winning_item].item;
  15.104 +		} else {
  15.105 +			/* No, take the first. */
  15.106 +			winning_item = results->item_list[0].item;
  15.107 +		}
  15.108 +
  15.109 +		unsigned int total_votes = results->num_votes;
  15.110 +		unsigned int winning_votes = results->item_list[0].count;
  15.111 +
  15.112 +		DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
  15.113 +	} else {
  15.114 +		IPluginContext *pContext = m_pVoteResults->GetParentContext();
  15.115 +		bool no_call = false;
  15.116 +		int err;
  15.117 +
  15.118 +		/* First array */
  15.119 +		cell_t client_array_address = -1;
  15.120 +		cell_t *client_array_base = NULL;
  15.121 +		cell_t client_array_size = results->num_clients + (results->num_clients * 2);
  15.122 +		if (client_array_size)
  15.123 +		{
  15.124 +			if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
  15.125 +				!= SP_ERROR_NONE)
  15.126 +			{
  15.127 +				g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
  15.128 +				no_call = true;
  15.129 +			} else {
  15.130 +				cell_t target_offs = sizeof(cell_t) * results->num_clients;
  15.131 +				cell_t *cur_index = client_array_base;
  15.132 +				cell_t *cur_array;
  15.133 +				for (unsigned int i=0; i<results->num_clients; i++)
  15.134 +				{
  15.135 +					/* Copy the array index */
  15.136 +					*cur_index = target_offs;
  15.137 +					/* Get the current array address */
  15.138 +					cur_array = (cell_t *)((char *)cur_index + target_offs);
  15.139 +					/* Store information */
  15.140 +					cur_array[0] = results->client_list[i].client;
  15.141 +					cur_array[1] = results->client_list[i].item;
  15.142 +					/* Adjust for the new target by subtracting one indirection
  15.143 +					 * and adding one array.
  15.144 +					 */
  15.145 +					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
  15.146 +					cur_index++;
  15.147 +				}
  15.148 +			}
  15.149 +		}
  15.150 +
  15.151 +		/* Second array */
  15.152 +		cell_t item_array_address = -1;
  15.153 +		cell_t *item_array_base = NULL;
  15.154 +		cell_t item_array_size = results->num_items + (results->num_items * 2);
  15.155 +		if (item_array_size)
  15.156 +		{
  15.157 +			if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
  15.158 +				!= SP_ERROR_NONE)
  15.159 +			{
  15.160 +				g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
  15.161 +				no_call = true;
  15.162 +			} else {
  15.163 +				cell_t target_offs = sizeof(cell_t) * results->num_items;
  15.164 +				cell_t *cur_index = item_array_base;
  15.165 +				cell_t *cur_array;
  15.166 +				for (unsigned int i=0; i<results->num_items; i++)
  15.167 +				{
  15.168 +					/* Copy the array index */
  15.169 +					*cur_index = target_offs;
  15.170 +					/* Get the current array address */
  15.171 +					cur_array = (cell_t *)((char *)cur_index + target_offs);
  15.172 +					/* Store information */
  15.173 +					cur_array[0] = results->item_list[i].item;
  15.174 +					cur_array[1] = results->item_list[i].count;
  15.175 +					/* Adjust for the new target by subtracting one indirection
  15.176 +					 * and adding one array.
  15.177 +					 */
  15.178 +					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
  15.179 +					cur_index++;
  15.180 +				}
  15.181 +			}
  15.182 +		}
  15.183 +
  15.184 +		/* Finally, push everything */
  15.185 +		if (!no_call)
  15.186 +		{
  15.187 +			m_pVoteResults->PushCell(results->num_votes);
  15.188 +			m_pVoteResults->PushCell(results->num_clients);
  15.189 +			m_pVoteResults->PushCell(client_array_address);
  15.190 +			m_pVoteResults->PushCell(results->num_items);
  15.191 +			m_pVoteResults->PushCell(item_array_address);
  15.192 +			m_pVoteResults->Execute(NULL);
  15.193 +		}
  15.194 +
  15.195 +		/* Free what we allocated, in reverse order as required */
  15.196 +		if (item_array_address != -1)
  15.197 +		{
  15.198 +			pContext->HeapPop(item_array_address);
  15.199 +		}
  15.200 +		if (client_array_address != -1)
  15.201 +		{
  15.202 +			pContext->HeapPop(client_array_address);
  15.203 +		}
  15.204 +	}
  15.205 +}
  15.206 +
  15.207 +bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
  15.208 +{
  15.209 +	if (strcmp(option, "set_vote_results_handler") == 0)
  15.210 +	{
  15.211 +		void **array = (void **)data;
  15.212 +		m_pVoteResults = (IPluginFunction *)array[0];
  15.213 +		m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
  15.214 +		return true;
  15.215 +	}
  15.216 +
  15.217 +	return false;
  15.218 +}
  15.219 +
  15.220  /**
  15.221   * INLINE FUNCTIONS FOR NATIVES
  15.222   */
  15.223 @@ -488,6 +621,11 @@
  15.224  
  15.225  static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
  15.226  {
  15.227 +	if (g_Menus.IsVoteInProgress())
  15.228 +	{
  15.229 +		return pContext->ThrowNativeError("A vote is already in progress");
  15.230 +	}
  15.231 +
  15.232  	Handle_t hndl = (Handle_t)params[1];
  15.233  	HandleError err;
  15.234  	IBaseMenu *menu;
  15.235 @@ -500,7 +638,12 @@
  15.236  	cell_t *addr;
  15.237  	pContext->LocalToPhysAddr(params[2], &addr);
  15.238  
  15.239 -	return menu->BroadcastVote(addr, params[3], params[4]) ? 1 : 0;
  15.240 +	if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
  15.241 +	{
  15.242 +		return 0;
  15.243 +	}
  15.244 +
  15.245 +	return 1;
  15.246  }
  15.247  
  15.248  static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
  15.249 @@ -699,7 +842,7 @@
  15.250  		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.251  	}
  15.252  
  15.253 -	return menu->GetExitButton() ? 1 : 0;
  15.254 +	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
  15.255  }
  15.256  
  15.257  static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
  15.258 @@ -713,7 +856,7 @@
  15.259  		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.260  	}
  15.261  
  15.262 -	return menu->GetExitBackButton() ? 1 : 0;
  15.263 +	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
  15.264  }
  15.265  
  15.266  static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
  15.267 @@ -727,7 +870,11 @@
  15.268  		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.269  	}
  15.270  
  15.271 -	return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
  15.272 +	unsigned int flags = menu->GetMenuOptionFlags();
  15.273 +	flags |= MENUFLAG_BUTTON_EXIT;
  15.274 +	menu->SetMenuOptionFlags(flags);
  15.275 +	flags = menu->GetMenuOptionFlags();
  15.276 +	return ((flags & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
  15.277  }
  15.278  
  15.279  static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
  15.280 @@ -741,7 +888,9 @@
  15.281  		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.282  	}
  15.283  
  15.284 -	menu->SetExitBackButton(params[2] ? true : false);
  15.285 +	unsigned int flags = menu->GetMenuOptionFlags();
  15.286 +	flags |= MENUFLAG_BUTTON_EXITBACK;
  15.287 +	menu->SetMenuOptionFlags(flags);
  15.288  
  15.289  	return 1;
  15.290  }
  15.291 @@ -757,23 +906,14 @@
  15.292  		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.293  	}
  15.294  
  15.295 -	menu->Cancel();
  15.296 +	g_Menus.CancelMenu(menu);
  15.297  
  15.298  	return 1;
  15.299  }
  15.300  
  15.301  static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
  15.302  {
  15.303 -	Handle_t hndl = (Handle_t)params[1];
  15.304 -	HandleError err;
  15.305 -	IBaseMenu *menu;
  15.306 -
  15.307 -	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  15.308 -	{
  15.309 -		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.310 -	}
  15.311 -
  15.312 -	return menu->IsVoteInProgress() ? 1 : 0;
  15.313 +	return g_Menus.IsVoteInProgress() ? 1 : 0;
  15.314  }
  15.315  
  15.316  static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
  15.317 @@ -1122,12 +1262,55 @@
  15.318  	return 1;
  15.319  }
  15.320  
  15.321 +static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
  15.322 +{
  15.323 +	if (!g_Menus.IsVoteInProgress())
  15.324 +	{
  15.325 +		return pContxt->ThrowNativeError("No vote is in progress");
  15.326 +	}
  15.327 +
  15.328 +	g_Menus.CancelVoting();
  15.329 +
  15.330 +	return 1;
  15.331 +}
  15.332 +
  15.333 +static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
  15.334 +{
  15.335 +	Handle_t hndl = (Handle_t)params[1];
  15.336 +	HandleError err;
  15.337 +	IBaseMenu *menu;
  15.338 +
  15.339 +	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  15.340 +	{
  15.341 +		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  15.342 +	}
  15.343 +
  15.344 +	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
  15.345 +	if (!pFunction)
  15.346 +	{
  15.347 +		return pContext->ThrowNativeError("Invalid function %x", params[2]);
  15.348 +	}
  15.349 +
  15.350 +	void *array[2];
  15.351 +	array[0] = pFunction;
  15.352 +	array[1] = (void *)&params[2];
  15.353 +
  15.354 +	IMenuHandler *pHandler = menu->GetHandler();
  15.355 +	if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
  15.356 +	{
  15.357 +		return pContext->ThrowNativeError("The given menu does not support this option");
  15.358 +	}
  15.359 +
  15.360 +	return 1;
  15.361 +}
  15.362 +
  15.363  REGISTER_NATIVES(menuNatives)
  15.364  {
  15.365  	{"AddMenuItem",				AddMenuItem},
  15.366  	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  15.367  	{"CancelClientMenu",		CancelClientMenu},
  15.368  	{"CancelMenu",				CancelMenu},
  15.369 +	{"CancelVote",				CancelVote},
  15.370  	{"CreateMenu",				CreateMenu},
  15.371  	{"CreateMenuEx",			CreateMenuEx},
  15.372  	{"CreatePanel",				CreatePanel},
  15.373 @@ -1161,6 +1344,7 @@
  15.374  	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  15.375  	{"SetPanelTitle",			SetPanelTitle},
  15.376  	{"SetPanelKeys",			SetPanelKeys},
  15.377 +	{"SetVoteResultCallback",	SetVoteResultCallback},
  15.378  	{"VoteMenu",				VoteMenu},
  15.379  	{NULL,						NULL},
  15.380  };
    16.1 --- a/core/vm/sp_vm_basecontext.cpp	Tue Aug 07 22:11:00 2007 +0000
    16.2 +++ b/core/vm/sp_vm_basecontext.cpp	Wed Aug 08 02:27:10 2007 +0000
    16.3 @@ -28,1031 +28,1031 @@
    16.4   * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    16.5   *
    16.6   * Version: $Id$
    16.7 - */
    16.8 -
    16.9 -#include <string.h>
   16.10 -#include <stdarg.h>
   16.11 -#include <assert.h>
   16.12 -#include <limits.h>
   16.13 -#include "sp_vm_api.h"
   16.14 -#include "sp_vm_basecontext.h"
   16.15 -#include "sp_vm_engine.h"
   16.16 -
   16.17 -#ifdef SOURCEMOD_BUILD
   16.18 -#include "Logger.h"
   16.19 -#endif
   16.20 -
   16.21 -using namespace SourcePawn;
   16.22 -
   16.23 -extern SourcePawnEngine g_SourcePawn;
   16.24 -
   16.25 -#define CELLBOUNDMAX	(INT_MAX/sizeof(cell_t))
   16.26 -#define STACKMARGIN		((cell_t)(16*sizeof(cell_t)))
   16.27 -
   16.28 -int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
   16.29 -{
   16.30 -	g_SourcePawn.RunTracer(ctx, frm, cip);
   16.31 -
   16.32 -	return SP_ERROR_NONE;
   16.33 -}
   16.34 -
   16.35 -BaseContext::BaseContext(sp_context_t *_ctx)
   16.36 -{
   16.37 -	ctx = _ctx;
   16.38 -	ctx->context = this;
   16.39 -	ctx->dbreak = GlobalDebugBreak;
   16.40 -	m_InExec = false;
   16.41 -	m_CustomMsg = false;
   16.42 -	m_funcsnum = ctx->vmbase->FunctionCount(ctx);
   16.43 -#if 0
   16.44 -	m_priv_funcs = NULL;
   16.45 -#endif
   16.46 -	m_pub_funcs = NULL;
   16.47 -
   16.48 -#if 0
   16.49 -	/**
   16.50 -	 * Note: Since the m_plugin member will never change,
   16.51 -	 * it is safe to assume the function count will never change
   16.52 -	 */
   16.53 -	if (m_funcsnum && m_priv_funcs == NULL)
   16.54 -	{
   16.55 -		m_priv_funcs = new CFunction *[m_funcsnum];
   16.56 -		memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
   16.57 -	} else {
   16.58 -		m_priv_funcs = NULL;
   16.59 -	}
   16.60 -#endif
   16.61 -
   16.62 -	if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
   16.63 -	{
   16.64 -		m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
   16.65 -		memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
   16.66 -	} else {
   16.67 -		m_pub_funcs = NULL;
   16.68 -	}
   16.69 -
   16.70 -	/* Initialize the null references */
   16.71 -	uint32_t index;
   16.72 -	if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
   16.73 -	{
   16.74 -		sp_pubvar_t *pubvar;
   16.75 -		GetPubvarByIndex(index, &pubvar);
   16.76 -		m_pNullVec = pubvar->offs;
   16.77 -	} else {
   16.78 -		m_pNullVec = NULL;
   16.79 -	}
   16.80 -
   16.81 -	if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
   16.82 -	{
   16.83 -		sp_pubvar_t *pubvar;
   16.84 -		GetPubvarByIndex(index, &pubvar);
   16.85 -		m_pNullString = pubvar->offs;
   16.86 -	} else {
   16.87 -		m_pNullString = NULL;
   16.88 -	}
   16.89 -}
   16.90 -
   16.91 -void BaseContext::FlushFunctionCache()
   16.92 -{
   16.93 -	if (m_pub_funcs)
   16.94 -	{
   16.95 -		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
   16.96 -		{
   16.97 -			delete m_pub_funcs[i];
   16.98 -			m_pub_funcs[i] = NULL;
   16.99 -		}
  16.100 -	}
  16.101 -
  16.102 -#if 0
  16.103 -	if (m_priv_funcs)
  16.104 -	{
  16.105 -		for (unsigned int i=0; i<m_funcsnum; i++)
  16.106 -		{
  16.107 -			delete m_priv_funcs[i];
  16.108 -			m_priv_funcs[i] = NULL;
  16.109 -		}
  16.110 -	}
  16.111 -#endif
  16.112 -}
  16.113 -
  16.114 -void BaseContext::RefreshFunctionCache()
  16.115 -{
  16.116 -	if (m_pub_funcs)
  16.117 -	{
  16.118 -		sp_public_t *pub;
  16.119 -		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
  16.120 -		{
  16.121 -			if (!m_pub_funcs[i])
  16.122 -			{
  16.123 -				continue;
  16.124 -			}
  16.125 -			if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
  16.126 -			{
  16.127 -				continue;
  16.128 -			}
  16.129 -			m_pub_funcs[i]->Set(pub->code_offs, this);
  16.130 -		}
  16.131 -	}
  16.132 -
  16.133 -#if 0
  16.134 -	if (m_priv_funcs)
  16.135 -	{
  16.136 -		for (unsigned int i=0; i<m_funcsnum; i++)
  16.137 -		{
  16.138 -			if (!m_priv_funcs[i])
  16.139 -			{
  16.140 -				continue;
  16.141 -			}
  16.142 -			g_pVM->
  16.143 -		}
  16.144 -	}
  16.145 -#endif
  16.146 -}
  16.147 -
  16.148 -BaseContext::~BaseContext()
  16.149 -{
  16.150 -	FlushFunctionCache();
  16.151 -	delete [] m_pub_funcs;
  16.152 -	m_pub_funcs = NULL;
  16.153 -#if 0
  16.154 -	delete [] m_priv_funcs;
  16.155 -	m_priv_funcs = NULL;
  16.156 -#endif
  16.157 -}
  16.158 -
  16.159 -void BaseContext::SetContext(sp_context_t *_ctx)
  16.160 -{
  16.161 -	if (!_ctx)
  16.162 -	{
  16.163 -		return;
  16.164 -	}
  16.165 -	ctx = _ctx;
  16.166 -	ctx->context = this;
  16.167 -	ctx->dbreak = GlobalDebugBreak;
  16.168 -	RefreshFunctionCache();
  16.169 -}
  16.170 -
  16.171 -IVirtualMachine *BaseContext::GetVirtualMachine()
  16.172 -{
  16.173 -	return (IVirtualMachine *)ctx->vmbase;
  16.174 -}
  16.175 -
  16.176 -sp_context_t *BaseContext::GetContext()
  16.177 -{
  16.178 -	return ctx;
  16.179 -}
  16.180 -
  16.181 -bool BaseContext::IsDebugging()
  16.182 -{
  16.183 -	return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
  16.184 -}
  16.185 -
  16.186 -int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
  16.187 -{
  16.188 -	if (!IsDebugging())
  16.189 -	{
  16.190 -		return SP_ERROR_NOTDEBUGGING;
  16.191 -	}
  16.192 -
  16.193 -	*oldpfn = ctx->dbreak;
  16.194 -	ctx->dbreak = newpfn;
  16.195 -
  16.196 -	return SP_ERROR_NONE;
  16.197 -}
  16.198 -
  16.199 -IPluginDebugInfo *BaseContext::GetDebugInfo()
  16.200 -{
  16.201 -	return this;
  16.202 -}
  16.203 -
  16.204 -int BaseContext::Execute(uint32_t code_addr, cell_t *result)
  16.205 -{
  16.206 -	if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
  16.207 -	{
  16.208 -		return SP_ERROR_NOT_RUNNABLE;
  16.209 -	}
  16.210 -
  16.211 -	/* tada, prevent a crash */
  16.212 -	cell_t _ignore_result;
  16.213 -	if (!result)
  16.214 -	{
  16.215 -		result = &_ignore_result;
  16.216 -	}
  16.217 -
  16.218 -	IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
  16.219 -
  16.220 -	uint32_t pushcount = ctx->pushcount;
  16.221 -	int err;
  16.222 -
  16.223 -	PushCell(pushcount++);
  16.224 -	ctx->pushcount = 0;
  16.225 -
  16.226 -	cell_t save_sp = ctx->sp;
  16.227 -	cell_t save_hp = ctx->hp;
  16.228 -	uint32_t n_idx = ctx->n_idx;
  16.229 -
  16.230 -	bool wasExec = m_InExec;
  16.231 -
  16.232 -	/* Clear the error state, if any */
  16.233 -	ctx->n_err = SP_ERROR_NONE;
  16.234 -	ctx->n_idx = 0;
  16.235 -	m_InExec = true;
  16.236 -	m_MsgCache[0] = '\0';
  16.237 -	m_CustomMsg = false;
  16.238 -
  16.239 -	g_SourcePawn.PushTracer(ctx);
  16.240 -
  16.241 -	err = vm->ContextExecute(ctx, code_addr, result);
  16.242 -
  16.243 -	m_InExec = wasExec;
  16.244 -
  16.245 -	/**
  16.246 -	 * :TODO: Calling from a plugin in here will erase the cached message...
  16.247 -	 * Should that be documented?
  16.248 -	 */
  16.249 -	g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
  16.250 -
  16.251 -#if 1//defined _DEBUG
  16.252 -	//:TODO: debug code for leak detection, remove before the release?
  16.253 -	if (err == SP_ERROR_NONE)
  16.254 -	{
  16.255 -		if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
  16.256 -		{
  16.257 -			const char *name;
  16.258 -			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
  16.259 -			g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
  16.260 -		}
  16.261 -		if (ctx->hp != save_hp)
  16.262 -		{
  16.263 -			const char *name;
  16.264 -			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
  16.265 -			g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
  16.266 -		}
  16.267 -		//assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
  16.268 -		//assert(ctx->hp == save_hp);
  16.269 -	}
  16.270 -#endif
  16.271 -	if (err != SP_ERROR_NONE)
  16.272 -	{
  16.273 -		ctx->sp = save_sp;
  16.274 -		ctx->hp = save_hp;
  16.275 -	}
  16.276 -
  16.277 -	ctx->n_idx = n_idx;
  16.278 -
  16.279 -	return err;
  16.280 -}
  16.281 -
  16.282 -void BaseContext::SetErrorMessage(const char *msg, va_list ap)
  16.283 -{
  16.284 -	m_CustomMsg = true;
  16.285 -
  16.286 -	vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
  16.287 -}
  16.288 -
  16.289 -cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
  16.290 -{
  16.291 -	if (!m_InExec)
  16.292 -	{
  16.293 -		return 0;
  16.294 -	}
  16.295 -
  16.296 -	ctx->n_err = error;
  16.297 -	
  16.298 -	if (msg)
  16.299 -	{
  16.300 -		va_list ap;
  16.301 -		va_start(ap, msg);
  16.302 -		SetErrorMessage(msg, ap);
  16.303 -		va_end(ap);
  16.304 -	}
  16.305 -
  16.306 -	return 0;
  16.307 -}
  16.308 -
  16.309 -cell_t BaseContext::ThrowNativeError(const char *msg, ...)
  16.310 -{
  16.311 -	if (!m_InExec)
  16.312 -	{
  16.313 -		return 0;
  16.314 -	}
  16.315 -
  16.316 -	ctx->n_err = SP_ERROR_NATIVE;
  16.317 -
  16.318 -	if (msg)
  16.319 -	{
  16.320 -		va_list ap;
  16.321 -		va_start(ap, msg);
  16.322 -		SetErrorMessage(msg, ap);
  16.323 -		va_end(ap);
  16.324 -	}
  16.325 -
  16.326 -	return 0;
  16.327 -}
  16.328 -
  16.329 -int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
  16.330 -{
  16.331 -	cell_t *addr;
  16.332 -	ucell_t realmem;
  16.333 -
  16.334 -#if 0
  16.335 -	if (cells > CELLBOUNDMAX)
  16.336 -	{
  16.337 -		return SP_ERROR_ARAM;
  16.338 -	}
  16.339 -#else
  16.340 -	assert(cells < CELLBOUNDMAX);
  16.341 -#endif
  16.342 -
  16.343 -	realmem = cells * sizeof(cell_t);
  16.344 -
  16.345 -	/**
  16.346 -	 * Check if the space between the heap and stack is sufficient.
  16.347 -	 */
  16.348 -	if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
  16.349 -	{
  16.350 -		return SP_ERROR_HEAPLOW;
  16.351 -	}
  16.352 -
  16.353 -	addr = (cell_t *)(ctx->memory + ctx->hp);
  16.354 -	/* store size of allocation in cells */
  16.355 -	*addr = (cell_t)cells;
  16.356 -	addr++;
  16.357 -	ctx->hp += sizeof(cell_t);
  16.358 -
  16.359 -	*local_addr = ctx->hp;
  16.360 -
  16.361 -	if (phys_addr)
  16.362 -	{
  16.363 -		*phys_addr = addr;
  16.364 -	}
  16.365 -
  16.366 -	ctx->hp += realmem;
  16.367 -
  16.368 -	return SP_ERROR_NONE;
  16.369 -}
  16.370 -
  16.371 -int BaseContext::HeapPop(cell_t local_addr)
  16.372 -{
  16.373 -	cell_t cellcount;
  16.374 -	cell_t *addr;
  16.375 -
  16.376 -	/* check the bounds of this address */
  16.377 -	local_addr -= sizeof(cell_t);
  16.378 -	if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
  16.379 -	{
  16.380 -		return SP_ERROR_INVALID_ADDRESS;
  16.381 -	}
  16.382 -
  16.383 -	addr = (cell_t *)(ctx->memory + local_addr);
  16.384 -	cellcount = (*addr) * sizeof(cell_t);
  16.385 -	/* check if this memory count looks valid */
  16.386 -	if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
  16.387 -	{
  16.388 -		return SP_ERROR_INVALID_ADDRESS;
  16.389 -	}
  16.390 -
  16.391 -	ctx->hp = local_addr;
  16.392 -
  16.393 -	return SP_ERROR_NONE;
  16.394 -}
  16.395 -
  16.396 -
  16.397 -int BaseContext::HeapRelease(cell_t local_addr)
  16.398 -{
  16.399 -	if (local_addr < ctx->heap_base)
  16.400 -	{
  16.401 -		return SP_ERROR_INVALID_ADDRESS;
  16.402 -	}
  16.403 -
  16.404 -	ctx->hp = local_addr - sizeof(cell_t);
  16.405 -
  16.406 -	return SP_ERROR_NONE;
  16.407 -}
  16.408 -
  16.409 -int BaseContext::FindNativeByName(const char *name, uint32_t *index)
  16.410 -{
  16.411 -	int high;
  16.412 -
  16.413 -	high = ctx->plugin->info.natives_num - 1;
  16.414 -
  16.415 -	for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
  16.416 -	{
  16.417 -		if (strcmp(ctx->natives[i].name, name) == 0)
  16.418 -		{
  16.419 -			if (index)
  16.420 -			{
  16.421 -				*index = i;
  16.422 -			}
  16.423 -			return SP_ERROR_NONE;
  16.424 -		}
  16.425 -	}
  16.426 -
  16.427 -	return SP_ERROR_NOT_FOUND;
  16.428 -}
  16.429 -
  16.430 -int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
  16.431 -{
  16.432 -	if (index >= ctx->plugin->info.natives_num)
  16.433 -	{
  16.434 -		return SP_ERROR_INDEX;
  16.435 -	}
  16.436 -
  16.437 -	if (native)
  16.438 -	{
  16.439 -		*native = &(ctx->natives[index]);
  16.440 -	}
  16.441 -
  16.442 -	return SP_ERROR_NONE;
  16.443 -}
  16.444 -
  16.445 -
  16.446 -uint32_t BaseContext::GetNativesNum()
  16.447 -{
  16.448 -	return ctx->plugin->info.natives_num;
  16.449 -}
  16.450 -
  16.451 -int BaseContext::FindPublicByName(const char *name, uint32_t *index)
  16.452 -{
  16.453 -	int diff, high, low;
  16.454 -	uint32_t mid;
  16.455 -
  16.456 -	high = ctx->plugin->info.publics_num - 1;
  16.457 -	low = 0;
  16.458 -
  16.459 -	while (low <= high)
  16.460 -	{
  16.461 -		mid = (low + high) / 2;
  16.462 -		diff = strcmp(ctx->publics[mid].name, name);
  16.463 -		if (diff == 0)
  16.464 -		{
  16.465 -			if (index)
  16.466 -			{
  16.467 -				*index = mid;
  16.468 -			}
  16.469 -			return SP_ERROR_NONE;
  16.470 -		} else if (diff < 0) {
  16.471 -			low = mid + 1;
  16.472 -		} else {
  16.473 -			high = mid - 1;
  16.474 -		}
  16.475 -	}
  16.476 -
  16.477 -	return SP_ERROR_NOT_FOUND;
  16.478 -}
  16.479 -
  16.480 -int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
  16.481 -{
  16.482 -	if (index >= ctx->plugin->info.publics_num)
  16.483 -	{
  16.484 -		return SP_ERROR_INDEX;
  16.485 -	}
  16.486 -
  16.487 -	if (pblic)
  16.488 -	{
  16.489 -		*pblic = &(ctx->publics[index]);
  16.490 -	}
  16.491 -
  16.492 -	return SP_ERROR_NONE;
  16.493 -}
  16.494 -
  16.495 -uint32_t BaseContext::GetPublicsNum()
  16.496 -{
  16.497 -	return ctx->plugin->info.publics_num;
  16.498 -}
  16.499 -
  16.500 -int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
  16.501 -{
  16.502 -	if (index >= ctx->plugin->info.pubvars_num)
  16.503 -	{
  16.504 -		return SP_ERROR_INDEX;
  16.505 -	}
  16.506 -
  16.507 -	if (pubvar)
  16.508 -	{
  16.509 -		*pubvar = &(ctx->pubvars[index]);
  16.510 -	}
  16.511 -
  16.512 -	return SP_ERROR_NONE;
  16.513 -}
  16.514 -
  16.515 -int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
  16.516 -{
  16.517 -	int diff, high, low;
  16.518 -	uint32_t mid;
  16.519 -
  16.520 -	high = ctx->plugin->info.pubvars_num - 1;
  16.521 -	low = 0;
  16.522 -
  16.523 -	while (low <= high)
  16.524 -	{
  16.525 -		mid = (low + high) / 2;
  16.526 -		diff = strcmp(ctx->pubvars[mid].name, name);
  16.527 -		if (diff == 0)
  16.528 -		{
  16.529 -			if (index)
  16.530 -			{
  16.531 -				*index = mid;
  16.532 -			}
  16.533 -			return SP_ERROR_NONE;
  16.534 -		} else if (diff < 0) {
  16.535 -			low = mid + 1;
  16.536 -		} else {
  16.537 -			high = mid - 1;
  16.538 -		}
  16.539 -	}
  16.540 -
  16.541 -	return SP_ERROR_NOT_FOUND;
  16.542 -}
  16.543 -
  16.544 -int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
  16.545 -{
  16.546 -	if (index >= ctx->plugin->info.pubvars_num)
  16.547 -	{
  16.548 -		return SP_ERROR_INDEX;
  16.549 -	}
  16.550 -
  16.551 -	*local_addr = ctx->plugin->info.pubvars[index].address;
  16.552 -	*phys_addr = ctx->pubvars[index].offs;
  16.553 -
  16.554 -	return SP_ERROR_NONE;
  16.555 -}
  16.556 -
  16.557 -uint32_t BaseContext::GetPubVarsNum()
  16.558 -{
  16.559 -	return ctx->plugin->info.pubvars_num;
  16.560 -}
  16.561 -
  16.562 -int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
  16.563 -{
  16.564 -	uint32_t i, j, max;
  16.565 -
  16.566 -	max = ctx->plugin->info.natives_num;
  16.567 -
  16.568 -	for (i=0; i<max; i++)
  16.569 -	{
  16.570 -		if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
  16.571 -		{
  16.572 -			continue;
  16.573 -		}
  16.574 -
  16.575 -		for (j=0; (natives[j].name) && (!num || j<num); j++)
  16.576 -		{
  16.577 -			if (!strcmp(ctx->natives[i].name, natives[j].name))
  16.578 -			{
  16.579 -				ctx->natives[i].pfn = natives[j].func;
  16.580 -				ctx->natives[i].status = SP_NATIVE_BOUND;
  16.581 -			}
  16.582 -		}
  16.583 -	}
  16.584 -
  16.585 -	return SP_ERROR_NONE;
  16.586 -}
  16.587 -
  16.588 -int BaseContext::BindNative(const sp_nativeinfo_t *native)
  16.589 -{
  16.590 -	uint32_t index;
  16.591 -	int err;
  16.592 -
  16.593 -	if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
  16.594 -	{
  16.595 -		return err;
  16.596 -	}
  16.597 -
  16.598 -	ctx->natives[index].pfn = native->func;
  16.599 -	ctx->natives[index].status = SP_NATIVE_BOUND;
  16.600 -
  16.601 -	return SP_ERROR_NONE;
  16.602 -}
  16.603 -
  16.604 -int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
  16.605 -{
  16.606 -	uint32_t nativesnum, i;
  16.607 -
  16.608 -	nativesnum = ctx->plugin->info.natives_num;
  16.609 -
  16.610 -	for (i=0; i<nativesnum; i++)
  16.611 -	{
  16.612 -		if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
  16.613 -		{
  16.614 -			ctx->natives[i].pfn = native;
  16.615 -			ctx->natives[i].status = SP_NATIVE_BOUND;
  16.616 -		}
  16.617 -	}
  16.618 -
  16.619 -	return SP_ERROR_NONE;
  16.620 -}
  16.621 -
  16.622 -int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
  16.623 -{
  16.624 -	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
  16.625 -	{
  16.626 -		return SP_ERROR_INVALID_ADDRESS;
  16.627 -	}
  16.628 -
  16.629 -	if (phys_addr)
  16.630 -	{
  16.631 -		*phys_addr = (cell_t *)(ctx->memory + local_addr);
  16.632 -	}
  16.633 -
  16.634 -	return SP_ERROR_NONE;
  16.635 -}
  16.636 -
  16.637 -int BaseContext::PushCell(cell_t value)
  16.638 -{
  16.639 -	if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
  16.640 -	{
  16.641 -		return SP_ERROR_STACKLOW;
  16.642 -	}
  16.643 -
  16.644 -	ctx->sp -= sizeof(cell_t);
  16.645 -	*(cell_t *)(ctx->memory + ctx->sp) = value;
  16.646 -	ctx->pushcount++;
  16.647 -
  16.648 -	return SP_ERROR_NONE;
  16.649 -}
  16.650 -
  16.651 -int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
  16.652 -{
  16.653 -	unsigned int i;
  16.654 -	int err;
  16.655 -
  16.656 -	for (i=0; i<numcells; i++)
  16.657 -	{
  16.658 -		if ((err = PushCell(array[i])) != SP_ERROR_NONE)
  16.659 -		{
  16.660 -			ctx->sp += (cell_t)(i * sizeof(cell_t));
  16.661 -			ctx->pushcount -= i;
  16.662 -			return err;
  16.663 -		}
  16.664 -	}
  16.665 -
  16.666 -	return SP_ERROR_NONE;
  16.667 -}
  16.668 -
  16.669 -int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
  16.670 -{
  16.671 -	cell_t *ph_addr;
  16.672 -	int err;
  16.673 -
  16.674 -	if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
  16.675 -	{
  16.676 -		return err;
  16.677 -	}
  16.678 -
  16.679 -	memcpy(ph_addr, array, numcells * sizeof(cell_t));
  16.680 -
  16.681 -	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
  16.682 -	{
  16.683 -		HeapRelease(*local_addr);
  16.684 -		return err;
  16.685 -	}
  16.686 -
  16.687 -	if (phys_addr)
  16.688 -	{
  16.689 -		*phys_addr = ph_addr;
  16.690 -	}
  16.691 -
  16.692 -	return SP_ERROR_NONE;
  16.693 -}
  16.694 -
  16.695 -int BaseContext::LocalToString(cell_t local_addr, char **addr)
  16.696 -{
  16.697 -	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
  16.698 -	{
  16.699 -		return SP_ERROR_INVALID_ADDRESS;
  16.700 -	}
  16.701 -	*addr = (char *)(ctx->memory + local_addr);
  16.702 -
  16.703 -	return SP_ERROR_NONE;
  16.704 -}
  16.705 -
  16.706 -int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
  16.707 -{
  16.708 -	char *ph_addr;
  16.709 -	int err;
  16.710 -	unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
  16.711 -
  16.712 -	if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
  16.713 -	{
  16.714 -		return err;
  16.715 -	}
  16.716 -
  16.717 -	memcpy(ph_addr, string, len);
  16.718 -	ph_addr[len] = '\0';
  16.719 -
  16.720 -	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
  16.721 -	{
  16.722 -		HeapRelease(*local_addr);
  16.723 -		return err;
  16.724 -	}
  16.725 -
  16.726 -	if (phys_addr)
  16.727 -	{
  16.728 -		*phys_addr = ph_addr;
  16.729 -	}
  16.730 -
  16.731 -	return SP_ERROR_NONE;
  16.732 -}
  16.733 -
  16.734 -int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
  16.735 -{
  16.736 -	char *dest;
  16.737 -	size_t len;
  16.738 -
  16.739 -	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
  16.740 -	{
  16.741 -		return SP_ERROR_INVALID_ADDRESS;
  16.742 -	}
  16.743 -
  16.744 -	if (bytes == 0)
  16.745 -	{
  16.746 -		return SP_ERROR_NONE;
  16.747 -	}
  16.748 -
  16.749 -	len = strlen(source);
  16.750 -	dest = (char *)(ctx->memory + local_addr);
  16.751 -
  16.752 -	if (len >= bytes)
  16.753 -	{
  16.754 -		len = bytes - 1;
  16.755 -	}
  16.756 -
  16.757 -	memcpy(dest, source, len);
  16.758 -	dest[len] = '\0';
  16.759 -
  16.760 -	return SP_ERROR_NONE;
  16.761 -}
  16.762 -
  16.763 -inline int __CheckValidChar(char *c)
  16.764 -{
  16.765 -	int count;
  16.766 -	int bytecount = 0;
  16.767 -
  16.768 -	for (count=1; (*c & 0xC0) == 0x80; count++)
  16.769 -	{
  16.770 -		c--;
  16.771 -	}
  16.772 -
  16.773 -	switch (*c & 0xF0)
  16.774 -	{
  16.775 -	case 0xC0:
  16.776 -	case 0xD0:
  16.777 -		{
  16.778 -			bytecount = 2;
  16.779 -			break;
  16.780 -		}
  16.781 -	case 0xE0:
  16.782 -		{
  16.783 -			bytecount = 3;
  16.784 -			break;
  16.785 -		}
  16.786 -	case 0xF0:
  16.787 -		{
  16.788 -			bytecount = 4;
  16.789 -			break;
  16.790 -		}
  16.791 -	}
  16.792 -
  16.793 -	if (bytecount != count)
  16.794 -	{
  16.795 -		return count;
  16.796 -	}
  16.797 -
  16.798 -	return 0;
  16.799 -}
  16.800 -
  16.801 -int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
  16.802 -{
  16.803 -	char *dest;
  16.804 -	size_t len;
  16.805 -	bool needtocheck = false;
  16.806 -
  16.807 -	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
  16.808 -	{
  16.809 -		return SP_ERROR_INVALID_ADDRESS;
  16.810 -	}
  16.811 -	
  16.812 -	if (maxbytes == 0)
  16.813 -	{
  16.814 -		return SP_ERROR_NONE;
  16.815 -	}
  16.816 -
  16.817 -	len = strlen(source);
  16.818 -	dest = (char *)(ctx->memory + local_addr);
  16.819 -
  16.820 -	if ((size_t)len >= maxbytes)
  16.821 -	{
  16.822 -		len = maxbytes - 1;
  16.823 -		needtocheck = true;
  16.824 -	}
  16.825 -
  16.826 -	memcpy(dest, source, len);
  16.827 -	if ((dest[len-1] & 1<<7) && needtocheck)
  16.828 -	{
  16.829 -		len -= __CheckValidChar(dest+len-1);
  16.830 -	}
  16.831 -	dest[len] = '\0';
  16.832 -
  16.833 -	if (wrtnbytes)
  16.834 -	{
  16.835 -		*wrtnbytes = len;
  16.836 -	}
  16.837 -
  16.838 -	return SP_ERROR_NONE;
  16.839 -}
  16.840 -
  16.841 -#define USHR(x) ((unsigned int)(x)>>1)
  16.842 -
  16.843 -int BaseContext::LookupFile(ucell_t addr, const char **filename)
  16.844 -{
  16.845 -	int high, low, mid;
  16.846 -
  16.847 -	high = ctx->plugin->debug.files_num;
  16.848 -	low = -1;
  16.849 -
  16.850 -	while (high - low > 1)
  16.851 -	{
  16.852 -		mid = USHR(low + high);
  16.853 -		if (ctx->files[mid].addr <= addr)
  16.854 -		{
  16.855 -			low = mid;
  16.856 -		} else {
  16.857 -			high = mid;
  16.858 -		}
  16.859 -	}
  16.860 -
  16.861 -	if (low == -1)
  16.862 -	{
  16.863 -		return SP_ERROR_NOT_FOUND;
  16.864 -	}
  16.865 -
  16.866 -	*filename = ctx->files[low].name;
  16.867 -
  16.868 -	return SP_ERROR_NONE;
  16.869 -}
  16.870 -
  16.871 -int BaseContext::LookupFunction(ucell_t addr, const char **name)
  16.872 -{
  16.873 -	uint32_t iter, max = ctx->plugin->debug.syms_num;
  16.874 -
  16.875 -	for (iter=0; iter<max; iter++)
  16.876 -	{
  16.877 -		if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) 
  16.878 -			&& (ctx->symbols[iter].codestart <= addr) 
  16.879 -			&& (ctx->symbols[iter].codeend > addr))
  16.880 -		{
  16.881 -			break;
  16.882 -		}
  16.883 -	}
  16.884 -
  16.885 -	if (iter >= max)
  16.886 -	{
  16.887 -		return SP_ERROR_NOT_FOUND;
  16.888 -	}
  16.889 -
  16.890 -	*name = ctx->symbols[iter].name;
  16.891 -
  16.892 -	return SP_ERROR_NONE;
  16.893 -}
  16.894 -
  16.895 -int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
  16.896 -{
  16.897 -	int high, low, mid;
  16.898 -
  16.899 -	high = ctx->plugin->debug.lines_num;
  16.900 -	low = -1;
  16.901 -
  16.902 -	while (high - low > 1)
  16.903 -	{
  16.904 -		mid = USHR(low + high);
  16.905 -		if (ctx->lines[mid].addr <= addr)
  16.906 -		{
  16.907 -			low = mid;
  16.908 -		} else {
  16.909 -			high = mid;
  16.910 -		}
  16.911 -	}
  16.912 -
  16.913 -	if (low == -1)
  16.914 -	{
  16.915 -		return SP_ERROR_NOT_FOUND;
  16.916 -	}
  16.917 -
  16.918 -	/* Since the CIP occurs BEFORE the line, we have to add one */
  16.919 -	*line = ctx->lines[low].line + 1;
  16.920 -
  16.921 -	return SP_ERROR_NONE;
  16.922 -}
  16.923 -
  16.924 -IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
  16.925 -{
  16.926 -	CFunction *pFunc = NULL;
  16.927 -
  16.928 -	if (func_id & 1)
  16.929 -	{
  16.930 -		func_id >>= 1;
  16.931 -		if (func_id >= ctx->plugin->info.publics_num)
  16.932 -		{
  16.933 -			return NULL;
  16.934 -		}
  16.935 -		pFunc = m_pub_funcs[func_id];
  16.936 -		if (!pFunc)
  16.937 -		{
  16.938 -			m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
  16.939 -			pFunc = m_pub_funcs[func_id];
  16.940 -		} else if (pFunc->IsInvalidated()) {
  16.941 -			pFunc->Set(ctx->publics[func_id].code_offs, this);
  16.942 -		}
  16.943 -	} else {
  16.944 -		/* :TODO: currently not used */
  16.945 -#if 0
  16.946 -		func_id >>= 1;
  16.947 -		unsigned int index;
  16.948 -		if (!g_pVM->FunctionLookup(ctx, func_id, &index))
  16.949 -		{
  16.950 -			return NULL;
  16.951 -		}
  16.952 -		pFunc = m_priv_funcs[func_id];
  16.953 -		if (!pFunc)
  16.954 -		{
  16.955 -			m_priv_funcs[func_id] = new CFunction(save, this);
  16.956 -			pFunc = m_priv_funcs[func_id];
  16.957 -		}
  16.958 -#endif
  16.959 -		assert(false);
  16.960 -	}
  16.961 -
  16.962 -	return pFunc;
  16.963 -}
  16.964 -
  16.965 -IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
  16.966 -{
  16.967 -	uint32_t index;
  16.968 -
  16.969 -	if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
  16.970 -	{
  16.971 -		return NULL;
  16.972 -	}
  16.973 -
  16.974 -	CFunction *pFunc = m_pub_funcs[index];
  16.975 -	if (!pFunc)
  16.976 -	{
  16.977 -		sp_public_t *pub = NULL;
  16.978 -		GetPublicByIndex(index, &pub);
  16.979 -		if (pub)
  16.980 -		{
  16.981 -			m_pub_funcs[index] = new CFunction(pub->code_offs, this);
  16.982 -		}
  16.983 -		pFunc = m_pub_funcs[index];
  16.984 -	} else if (pFunc->IsInvalidated()) {
  16.985 -		sp_public_t *pub = NULL;
  16.986 -		GetPublicByIndex(index, &pub);
  16.987 -		if (pub)
  16.988 -		{
  16.989 -			pFunc->Set(pub->code_offs, this);
  16.990 -		} else {
  16.991 -			pFunc = NULL;
  16.992 -		}
  16.993 -	}
  16.994 -
  16.995 -	return pFunc;
  16.996 -}
  16.997 -
  16.998 -int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
  16.999 -{
 16.1000 -	int err;
 16.1001 -	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
 16.1002 -	{
 16.1003 -		return err;
 16.1004 -	}
 16.1005 -
 16.1006 -	if ((cell_t *)*addr == m_pNullString)
 16.1007 -	{
 16.1008 -		*addr = NULL;
 16.1009 -	}
 16.1010 -
 16.1011 -	return SP_ERROR_NONE;
 16.1012 -}
 16.1013 -
 16.1014 -#if defined SOURCEMOD_BUILD
 16.1015 -SourceMod::IdentityToken_t *BaseContext::GetIdentity()
 16.1016 -{
 16.1017 -	return m_pToken;
 16.1018 -}
 16.1019 -
 16.1020 -void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
 16.1021 -{
 16.1022 -	m_pToken = token;
 16.1023 -}
 16.1024 -
 16.1025 -cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
 16.1026 -{
 16.1027 -	if (type == SP_NULL_VECTOR)
 16.1028 -	{
 16.1029 -		return m_pNullVec;
 16.1030 -	}
 16.1031 -
 16.1032 -	return NULL;
 16.1033 -}
 16.1034 -#endif
 16.1035 + */
 16.1036 +
 16.1037 +#include <string.h>
 16.1038 +#include <stdarg.h>
 16.1039 +#include <assert.h>
 16.1040 +#include <limits.h>
 16.1041 +#include "sp_vm_api.h"
 16.1042 +#include "sp_vm_basecontext.h"
 16.1043 +#include "sp_vm_engine.h"
 16.1044 +
 16.1045 +#ifdef SOURCEMOD_BUILD
 16.1046 +#include "Logger.h"
 16.1047 +#endif
 16.1048 +
 16.1049 +using namespace SourcePawn;
 16.1050 +
 16.1051 +extern SourcePawnEngine g_SourcePawn;
 16.1052 +
 16.1053 +#define CELLBOUNDMAX	(INT_MAX/sizeof(cell_t))
 16.1054 +#define STACKMARGIN		((cell_t)(16*sizeof(cell_t)))
 16.1055 +
 16.1056 +int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
 16.1057 +{
 16.1058 +	g_SourcePawn.RunTracer(ctx, frm, cip);
 16.1059 +
 16.1060 +	return SP_ERROR_NONE;
 16.1061 +}
 16.1062 +
 16.1063 +BaseContext::BaseContext(sp_context_t *_ctx)
 16.1064 +{
 16.1065 +	ctx = _ctx;
 16.1066 +	ctx->context = this;
 16.1067 +	ctx->dbreak = GlobalDebugBreak;
 16.1068 +	m_InExec = false;
 16.1069 +	m_CustomMsg = false;
 16.1070 +	m_funcsnum = ctx->vmbase->FunctionCount(ctx);
 16.1071 +#if 0
 16.1072 +	m_priv_funcs = NULL;
 16.1073 +#endif
 16.1074 +	m_pub_funcs = NULL;
 16.1075 +
 16.1076 +#if 0
 16.1077 +	/**
 16.1078 +	 * Note: Since the m_plugin member will never change,
 16.1079 +	 * it is safe to assume the function count will never change
 16.1080 +	 */
 16.1081 +	if (m_funcsnum && m_priv_funcs == NULL)
 16.1082 +	{
 16.1083 +		m_priv_funcs = new CFunction *[m_funcsnum];
 16.1084 +		memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
 16.1085 +	} else {
 16.1086 +		m_priv_funcs = NULL;
 16.1087 +	}
 16.1088 +#endif
 16.1089 +
 16.1090 +	if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
 16.1091 +	{
 16.1092 +		m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
 16.1093 +		memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
 16.1094 +	} else {
 16.1095 +		m_pub_funcs = NULL;
 16.1096 +	}
 16.1097 +
 16.1098 +	/* Initialize the null references */
 16.1099 +	uint32_t index;
 16.1100 +	if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
 16.1101 +	{
 16.1102 +		sp_pubvar_t *pubvar;
 16.1103 +		GetPubvarByIndex(index, &pubvar);
 16.1104 +		m_pNullVec = pubvar->offs;
 16.1105 +	} else {
 16.1106 +		m_pNullVec = NULL;
 16.1107 +	}
 16.1108 +
 16.1109 +	if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
 16.1110 +	{
 16.1111 +		sp_pubvar_t *pubvar;
 16.1112 +		GetPubvarByIndex(index, &pubvar);
 16.1113 +		m_pNullString = pubvar->offs;
 16.1114 +	} else {
 16.1115 +		m_pNullString = NULL;
 16.1116 +	}
 16.1117 +}
 16.1118 +
 16.1119 +void BaseContext::FlushFunctionCache()
 16.1120 +{
 16.1121 +	if (m_pub_funcs)
 16.1122 +	{
 16.1123 +		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
 16.1124 +		{
 16.1125 +			delete m_pub_funcs[i];
 16.1126 +			m_pub_funcs[i] = NULL;
 16.1127 +		}
 16.1128 +	}
 16.1129 +
 16.1130 +#if 0
 16.1131 +	if (m_priv_funcs)
 16.1132 +	{
 16.1133 +		for (unsigned int i=0; i<m_funcsnum; i++)
 16.1134 +		{
 16.1135 +			delete m_priv_funcs[i];
 16.1136 +			m_priv_funcs[i] = NULL;
 16.1137 +		}
 16.1138 +	}
 16.1139 +#endif
 16.1140 +}
 16.1141 +
 16.1142 +void BaseContext::RefreshFunctionCache()
 16.1143 +{
 16.1144 +	if (m_pub_funcs)
 16.1145 +	{
 16.1146 +		sp_public_t *pub;
 16.1147 +		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
 16.1148 +		{
 16.1149 +			if (!m_pub_funcs[i])
 16.1150 +			{
 16.1151 +				continue;
 16.1152 +			}
 16.1153 +			if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
 16.1154 +			{
 16.1155 +				continue;
 16.1156 +			}
 16.1157 +			m_pub_funcs[i]->Set(pub->code_offs, this);
 16.1158 +		}
 16.1159 +	}
 16.1160 +
 16.1161 +#if 0
 16.1162 +	if (m_priv_funcs)
 16.1163 +	{
 16.1164 +		for (unsigned int i=0; i<m_funcsnum; i++)
 16.1165 +		{
 16.1166 +			if (!m_priv_funcs[i])
 16.1167 +			{
 16.1168 +				continue;
 16.1169 +			}
 16.1170 +			g_pVM->
 16.1171 +		}
 16.1172 +	}
 16.1173 +#endif
 16.1174 +}
 16.1175 +
 16.1176 +BaseContext::~BaseContext()
 16.1177 +{
 16.1178 +	FlushFunctionCache();
 16.1179 +	delete [] m_pub_funcs;
 16.1180 +	m_pub_funcs = NULL;
 16.1181 +#if 0
 16.1182 +	delete [] m_priv_funcs;
 16.1183 +	m_priv_funcs = NULL;
 16.1184 +#endif
 16.1185 +}
 16.1186 +
 16.1187 +void BaseContext::SetContext(sp_context_t *_ctx)
 16.1188 +{
 16.1189 +	if (!_ctx)
 16.1190 +	{
 16.1191 +		return;
 16.1192 +	}
 16.1193 +	ctx = _ctx;
 16.1194 +	ctx->context = this;
 16.1195 +	ctx->dbreak = GlobalDebugBreak;
 16.1196 +	RefreshFunctionCache();
 16.1197 +}
 16.1198 +
 16.1199 +IVirtualMachine *BaseContext::GetVirtualMachine()
 16.1200 +{
 16.1201 +	return (IVirtualMachine *)ctx->vmbase;
 16.1202 +}
 16.1203 +
 16.1204 +sp_context_t *BaseContext::GetContext()
 16.1205 +{
 16.1206 +	return ctx;
 16.1207 +}
 16.1208 +
 16.1209 +bool BaseContext::IsDebugging()
 16.1210 +{
 16.1211 +	return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
 16.1212 +}
 16.1213 +
 16.1214 +int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
 16.1215 +{
 16.1216 +	if (!IsDebugging())
 16.1217 +	{
 16.1218 +		return SP_ERROR_NOTDEBUGGING;
 16.1219 +	}
 16.1220 +
 16.1221 +	*oldpfn = ctx->dbreak;
 16.1222 +	ctx->dbreak = newpfn;
 16.1223 +
 16.1224 +	return SP_ERROR_NONE;
 16.1225 +}
 16.1226 +
 16.1227 +IPluginDebugInfo *BaseContext::GetDebugInfo()
 16.1228 +{
 16.1229 +	return this;
 16.1230 +}
 16.1231 +
 16.1232 +int BaseContext::Execute(uint32_t code_addr, cell_t *result)
 16.1233 +{
 16.1234 +	if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
 16.1235 +	{
 16.1236 +		return SP_ERROR_NOT_RUNNABLE;
 16.1237 +	}
 16.1238 +
 16.1239 +	/* tada, prevent a crash */
 16.1240 +	cell_t _ignore_result;
 16.1241 +	if (!result)
 16.1242 +	{
 16.1243 +		result = &_ignore_result;
 16.1244 +	}
 16.1245 +
 16.1246 +	IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
 16.1247 +
 16.1248 +	uint32_t pushcount = ctx->pushcount;
 16.1249 +	int err;
 16.1250 +
 16.1251 +	PushCell(pushcount++);
 16.1252 +	ctx->pushcount = 0;
 16.1253 +
 16.1254 +	cell_t save_sp = ctx->sp;
 16.1255 +	cell_t save_hp = ctx->hp;
 16.1256 +	uint32_t n_idx = ctx->n_idx;
 16.1257 +
 16.1258 +	bool wasExec = m_InExec;
 16.1259 +
 16.1260 +	/* Clear the error state, if any */
 16.1261 +	ctx->n_err = SP_ERROR_NONE;
 16.1262 +	ctx->n_idx = 0;
 16.1263 +	m_InExec = true;
 16.1264 +	m_MsgCache[0] = '\0';
 16.1265 +	m_CustomMsg = false;
 16.1266 +
 16.1267 +	g_SourcePawn.PushTracer(ctx);
 16.1268 +
 16.1269 +	err = vm->ContextExecute(ctx, code_addr, result);
 16.1270 +
 16.1271 +	m_InExec = wasExec;
 16.1272 +
 16.1273 +	/**
 16.1274 +	 * :TODO: Calling from a plugin in here will erase the cached message...
 16.1275 +	 * Should that be documented?
 16.1276 +	 */
 16.1277 +	g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
 16.1278 +
 16.1279 +#if 1//defined _DEBUG
 16.1280 +	//:TODO: debug code for leak detection, remove before the release?
 16.1281 +	if (err == SP_ERROR_NONE)
 16.1282 +	{
 16.1283 +		if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
 16.1284 +		{
 16.1285 +			const char *name;
 16.1286 +			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
 16.1287 +			g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
 16.1288 +		}
 16.1289 +		if (ctx->hp != save_hp)
 16.1290 +		{
 16.1291 +			const char *name;
 16.1292 +			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
 16.1293 +			g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
 16.1294 +		}
 16.1295 +		//assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
 16.1296 +		//assert(ctx->hp == save_hp);
 16.1297 +	}
 16.1298 +#endif
 16.1299 +	if (err != SP_ERROR_NONE)
 16.1300 +	{
 16.1301 +		ctx->sp = save_sp;
 16.1302 +		ctx->hp = save_hp;
 16.1303 +	}
 16.1304 +
 16.1305 +	ctx->n_idx = n_idx;
 16.1306 +
 16.1307 +	return err;
 16.1308 +}
 16.1309 +
 16.1310 +void BaseContext::SetErrorMessage(const char *msg, va_list ap)
 16.1311 +{
 16.1312 +	m_CustomMsg = true;
 16.1313 +
 16.1314 +	vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
 16.1315 +}
 16.1316 +
 16.1317 +cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
 16.1318 +{
 16.1319 +	if (!m_InExec)
 16.1320 +	{
 16.1321 +		return 0;
 16.1322 +	}
 16.1323 +
 16.1324 +	ctx->n_err = error;
 16.1325 +	
 16.1326 +	if (msg)
 16.1327 +	{
 16.1328 +		va_list ap;
 16.1329 +		va_start(ap, msg);
 16.1330 +		SetErrorMessage(msg, ap);
 16.1331 +		va_end(ap);
 16.1332 +	}
 16.1333 +
 16.1334 +	return 0;
 16.1335 +}
 16.1336 +
 16.1337 +cell_t BaseContext::ThrowNativeError(const char *msg, ...)
 16.1338 +{
 16.1339 +	if (!m_InExec)
 16.1340 +	{
 16.1341 +		return 0;
 16.1342 +	}
 16.1343 +
 16.1344 +	ctx->n_err = SP_ERROR_NATIVE;
 16.1345 +
 16.1346 +	if (msg)
 16.1347 +	{
 16.1348 +		va_list ap;
 16.1349 +		va_start(ap, msg);
 16.1350 +		SetErrorMessage(msg, ap);
 16.1351 +		va_end(ap);
 16.1352 +	}
 16.1353 +
 16.1354 +	return 0;
 16.1355 +}
 16.1356 +
 16.1357 +int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
 16.1358 +{
 16.1359 +	cell_t *addr;
 16.1360 +	ucell_t realmem;
 16.1361 +
 16.1362 +#if 0
 16.1363 +	if (cells > CELLBOUNDMAX)
 16.1364 +	{
 16.1365 +		return SP_ERROR_ARAM;
 16.1366 +	}
 16.1367 +#else
 16.1368 +	assert(cells < CELLBOUNDMAX);
 16.1369 +#endif
 16.1370 +
 16.1371 +	realmem = cells * sizeof(cell_t);
 16.1372 +
 16.1373 +	/**
 16.1374 +	 * Check if the space between the heap and stack is sufficient.
 16.1375 +	 */
 16.1376 +	if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
 16.1377 +	{
 16.1378 +		return SP_ERROR_HEAPLOW;
 16.1379 +	}
 16.1380 +
 16.1381 +	addr = (cell_t *)(ctx->memory + ctx->hp);
 16.1382 +	/* store size of allocation in cells */
 16.1383 +	*addr = (cell_t)cells;
 16.1384 +	addr++;
 16.1385 +	ctx->hp += sizeof(cell_t);
 16.1386 +
 16.1387 +	*local_addr = ctx->hp;
 16.1388 +
 16.1389 +	if (phys_addr)
 16.1390 +	{
 16.1391 +		*phys_addr = addr;
 16.1392 +	}
 16.1393 +
 16.1394 +	ctx->hp += realmem;
 16.1395 +
 16.1396 +	return SP_ERROR_NONE;
 16.1397 +}
 16.1398 +
 16.1399 +int BaseContext::HeapPop(cell_t local_addr)
 16.1400 +{
 16.1401 +	cell_t cellcount;
 16.1402 +	cell_t *addr;
 16.1403 +
 16.1404 +	/* check the bounds of this address */
 16.1405 +	local_addr -= sizeof(cell_t);
 16.1406 +	if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
 16.1407 +	{
 16.1408 +		return SP_ERROR_INVALID_ADDRESS;
 16.1409 +	}
 16.1410 +
 16.1411 +	addr = (cell_t *)(ctx->memory + local_addr);
 16.1412 +	cellcount = (*addr) * sizeof(cell_t);
 16.1413 +	/* check if this memory count looks valid */
 16.1414 +	if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
 16.1415 +	{
 16.1416 +		return SP_ERROR_INVALID_ADDRESS;
 16.1417 +	}
 16.1418 +
 16.1419 +	ctx->hp = local_addr;
 16.1420 +
 16.1421 +	return SP_ERROR_NONE;
 16.1422 +}
 16.1423 +
 16.1424 +
 16.1425 +int BaseContext::HeapRelease(cell_t local_addr)
 16.1426 +{
 16.1427 +	if (local_addr < ctx->heap_base)
 16.1428 +	{
 16.1429 +		return SP_ERROR_INVALID_ADDRESS;
 16.1430 +	}
 16.1431 +
 16.1432 +	ctx->hp = local_addr - sizeof(cell_t);
 16.1433 +
 16.1434 +	return SP_ERROR_NONE;
 16.1435 +}
 16.1436 +
 16.1437 +int BaseContext::FindNativeByName(const char *name, uint32_t *index)
 16.1438 +{
 16.1439 +	int high;
 16.1440 +
 16.1441 +	high = ctx->plugin->info.natives_num - 1;
 16.1442 +
 16.1443 +	for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
 16.1444 +	{
 16.1445 +		if (strcmp(ctx->natives[i].name, name) == 0)
 16.1446 +		{
 16.1447 +			if (index)
 16.1448 +			{
 16.1449 +				*index = i;
 16.1450 +			}
 16.1451 +			return SP_ERROR_NONE;
 16.1452 +		}
 16.1453 +	}
 16.1454 +
 16.1455 +	return SP_ERROR_NOT_FOUND;
 16.1456 +}
 16.1457 +
 16.1458 +int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
 16.1459 +{
 16.1460 +	if (index >= ctx->plugin->info.natives_num)
 16.1461 +	{
 16.1462 +		return SP_ERROR_INDEX;
 16.1463 +	}
 16.1464 +
 16.1465 +	if (native)
 16.1466 +	{
 16.1467 +		*native = &(ctx->natives[index]);
 16.1468 +	}
 16.1469 +
 16.1470 +	return SP_ERROR_NONE;
 16.1471 +}
 16.1472 +
 16.1473 +
 16.1474 +uint32_t BaseContext::GetNativesNum()
 16.1475 +{
 16.1476 +	return ctx->plugin->info.natives_num;
 16.1477 +}
 16.1478 +
 16.1479 +int BaseContext::FindPublicByName(const char *name, uint32_t *index)
 16.1480 +{
 16.1481 +	int diff, high, low;
 16.1482 +	uint32_t mid;
 16.1483 +
 16.1484 +	high = ctx->plugin->info.publics_num - 1;
 16.1485 +	low = 0;
 16.1486 +
 16.1487 +	while (low <= high)
 16.1488 +	{
 16.1489 +		mid = (low + high) / 2;
 16.1490 +		diff = strcmp(ctx->publics[mid].name, name);
 16.1491 +		if (diff == 0)
 16.1492 +		{
 16.1493 +			if (index)
 16.1494 +			{
 16.1495 +				*index = mid;
 16.1496 +			}
 16.1497 +			return SP_ERROR_NONE;
 16.1498 +		} else if (diff < 0) {
 16.1499 +			low = mid + 1;
 16.1500 +		} else {
 16.1501 +			high = mid - 1;
 16.1502 +		}
 16.1503 +	}
 16.1504 +
 16.1505 +	return SP_ERROR_NOT_FOUND;
 16.1506 +}
 16.1507 +
 16.1508 +int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
 16.1509 +{
 16.1510 +	if (index >= ctx->plugin->info.publics_num)
 16.1511 +	{
 16.1512 +		return SP_ERROR_INDEX;
 16.1513 +	}
 16.1514 +
 16.1515 +	if (pblic)
 16.1516 +	{
 16.1517 +		*pblic = &(ctx->publics[index]);
 16.1518 +	}
 16.1519 +
 16.1520 +	return SP_ERROR_NONE;
 16.1521 +}
 16.1522 +
 16.1523 +uint32_t BaseContext::GetPublicsNum()
 16.1524 +{
 16.1525 +	return ctx->plugin->info.publics_num;
 16.1526 +}
 16.1527 +
 16.1528 +int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
 16.1529 +{
 16.1530 +	if (index >= ctx->plugin->info.pubvars_num)
 16.1531 +	{
 16.1532 +		return SP_ERROR_INDEX;
 16.1533 +	}
 16.1534 +
 16.1535 +	if (pubvar)
 16.1536 +	{
 16.1537 +		*pubvar = &(ctx->pubvars[index]);
 16.1538 +	}
 16.1539 +
 16.1540 +	return SP_ERROR_NONE;
 16.1541 +}
 16.1542 +
 16.1543 +int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
 16.1544 +{
 16.1545 +	int diff, high, low;
 16.1546 +	uint32_t mid;
 16.1547 +
 16.1548 +	high = ctx->plugin->info.pubvars_num - 1;
 16.1549 +	low = 0;
 16.1550 +
 16.1551 +	while (low <= high)
 16.1552 +	{
 16.1553 +		mid = (low + high) / 2;
 16.1554 +		diff = strcmp(ctx->pubvars[mid].name, name);
 16.1555 +		if (diff == 0)
 16.1556 +		{
 16.1557 +			if (index)
 16.1558 +			{
 16.1559 +				*index = mid;
 16.1560 +			}
 16.1561 +			return SP_ERROR_NONE;
 16.1562 +		} else if (diff < 0) {
 16.1563 +			low = mid + 1;
 16.1564 +		} else {
 16.1565 +			high = mid - 1;
 16.1566 +		}
 16.1567 +	}
 16.1568 +
 16.1569 +	return SP_ERROR_NOT_FOUND;
 16.1570 +}
 16.1571 +
 16.1572 +int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
 16.1573 +{
 16.1574 +	if (index >= ctx->plugin->info.pubvars_num)
 16.1575 +	{
 16.1576 +		return SP_ERROR_INDEX;
 16.1577 +	}
 16.1578 +
 16.1579 +	*local_addr = ctx->plugin->info.pubvars[index].address;
 16.1580 +	*phys_addr = ctx->pubvars[index].offs;
 16.1581 +
 16.1582 +	return SP_ERROR_NONE;
 16.1583 +}
 16.1584 +
 16.1585 +uint32_t BaseContext::GetPubVarsNum()
 16.1586 +{
 16.1587 +	return ctx->plugin->info.pubvars_num;
 16.1588 +}
 16.1589 +
 16.1590 +int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
 16.1591 +{
 16.1592 +	uint32_t i, j, max;
 16.1593 +
 16.1594 +	max = ctx->plugin->info.natives_num;
 16.1595 +
 16.1596 +	for (i=0; i<max; i++)
 16.1597 +	{
 16.1598 +		if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
 16.1599 +		{
 16.1600 +			continue;
 16.1601 +		}
 16.1602 +
 16.1603 +		for (j=0; (natives[j].name) && (!num || j<num); j++)
 16.1604 +		{
 16.1605 +			if (!strcmp(ctx->natives[i].name, natives[j].name))
 16.1606 +			{
 16.1607 +				ctx->natives[i].pfn = natives[j].func;
 16.1608 +				ctx->natives[i].status = SP_NATIVE_BOUND;
 16.1609 +			}
 16.1610 +		}
 16.1611 +	}
 16.1612 +
 16.1613 +	return SP_ERROR_NONE;
 16.1614 +}
 16.1615 +
 16.1616 +int BaseContext::BindNative(const sp_nativeinfo_t *native)
 16.1617 +{
 16.1618 +	uint32_t index;
 16.1619 +	int err;
 16.1620 +
 16.1621 +	if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
 16.1622 +	{
 16.1623 +		return err;
 16.1624 +	}
 16.1625 +
 16.1626 +	ctx->natives[index].pfn = native->func;
 16.1627 +	ctx->natives[index].status = SP_NATIVE_BOUND;
 16.1628 +
 16.1629 +	return SP_ERROR_NONE;
 16.1630 +}
 16.1631 +
 16.1632 +int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
 16.1633 +{
 16.1634 +	uint32_t nativesnum, i;
 16.1635 +
 16.1636 +	nativesnum = ctx->plugin->info.natives_num;
 16.1637 +
 16.1638 +	for (i=0; i<nativesnum; i++)
 16.1639 +	{
 16.1640 +		if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
 16.1641 +		{
 16.1642 +			ctx->natives[i].pfn = native;
 16.1643 +			ctx->natives[i].status = SP_NATIVE_BOUND;
 16.1644 +		}
 16.1645 +	}
 16.1646 +
 16.1647 +	return SP_ERROR_NONE;
 16.1648 +}
 16.1649 +
 16.1650 +int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
 16.1651 +{
 16.1652 +	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
 16.1653 +	{
 16.1654 +		return SP_ERROR_INVALID_ADDRESS;
 16.1655 +	}
 16.1656 +
 16.1657 +	if (phys_addr)
 16.1658 +	{
 16.1659 +		*phys_addr = (cell_t *)(ctx->memory + local_addr);
 16.1660 +	}
 16.1661 +
 16.1662 +	return SP_ERROR_NONE;
 16.1663 +}
 16.1664 +
 16.1665 +int BaseContext::PushCell(cell_t value)
 16.1666 +{
 16.1667 +	if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
 16.1668 +	{
 16.1669 +		return SP_ERROR_STACKLOW;
 16.1670 +	}
 16.1671 +
 16.1672 +	ctx->sp -= sizeof(cell_t);
 16.1673 +	*(cell_t *)(ctx->memory + ctx->sp) = value;
 16.1674 +	ctx->pushcount++;
 16.1675 +
 16.1676 +	return SP_ERROR_NONE;
 16.1677 +}
 16.1678 +
 16.1679 +int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
 16.1680 +{
 16.1681 +	unsigned int i;
 16.1682 +	int err;
 16.1683 +
 16.1684 +	for (i=0; i<numcells; i++)
 16.1685 +	{
 16.1686 +		if ((err = PushCell(array[i])) != SP_ERROR_NONE)
 16.1687 +		{
 16.1688 +			ctx->sp += (cell_t)(i * sizeof(cell_t));
 16.1689 +			ctx->pushcount -= i;
 16.1690 +			return err;
 16.1691 +		}
 16.1692 +	}
 16.1693 +
 16.1694 +	return SP_ERROR_NONE;
 16.1695 +}
 16.1696 +
 16.1697 +int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
 16.1698 +{
 16.1699 +	cell_t *ph_addr;
 16.1700 +	int err;
 16.1701 +
 16.1702 +	if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
 16.1703 +	{
 16.1704 +		return err;
 16.1705 +	}
 16.1706 +
 16.1707 +	memcpy(ph_addr, array, numcells * sizeof(cell_t));
 16.1708 +
 16.1709 +	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
 16.1710 +	{
 16.1711 +		HeapRelease(*local_addr);
 16.1712 +		return err;
 16.1713 +	}
 16.1714 +
 16.1715 +	if (phys_addr)
 16.1716 +	{
 16.1717 +		*phys_addr = ph_addr;
 16.1718 +	}
 16.1719 +
 16.1720 +	return SP_ERROR_NONE;
 16.1721 +}
 16.1722 +
 16.1723 +int BaseContext::LocalToString(cell_t local_addr, char **addr)
 16.1724 +{
 16.1725 +	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
 16.1726 +	{
 16.1727 +		return SP_ERROR_INVALID_ADDRESS;
 16.1728 +	}
 16.1729 +	*addr = (char *)(ctx->memory + local_addr);
 16.1730 +
 16.1731 +	return SP_ERROR_NONE;
 16.1732 +}
 16.1733 +
 16.1734 +int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
 16.1735 +{
 16.1736 +	char *ph_addr;
 16.1737 +	int err;
 16.1738 +	unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
 16.1739 +
 16.1740 +	if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
 16.1741 +	{
 16.1742 +		return err;
 16.1743 +	}
 16.1744 +
 16.1745 +	memcpy(ph_addr, string, len);
 16.1746 +	ph_addr[len] = '\0';
 16.1747 +
 16.1748 +	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
 16.1749 +	{
 16.1750 +		HeapRelease(*local_addr);
 16.1751 +		return err;
 16.1752 +	}
 16.1753 +
 16.1754 +	if (phys_addr)
 16.1755 +	{
 16.1756 +		*phys_addr = ph_addr;
 16.1757 +	}
 16.1758 +
 16.1759 +	return SP_ERROR_NONE;
 16.1760 +}
 16.1761 +
 16.1762 +int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
 16.1763 +{
 16.1764 +	char *dest;
 16.1765 +	size_t len;
 16.1766 +
 16.1767 +	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
 16.1768 +	{
 16.1769 +		return SP_ERROR_INVALID_ADDRESS;
 16.1770 +	}
 16.1771 +
 16.1772 +	if (bytes == 0)
 16.1773 +	{
 16.1774 +		return SP_ERROR_NONE;
 16.1775 +	}
 16.1776 +
 16.1777 +	len = strlen(source);
 16.1778 +	dest = (char *)(ctx->memory + local_addr);
 16.1779 +
 16.1780 +	if (len >= bytes)
 16.1781 +	{
 16.1782 +		len = bytes - 1;
 16.1783 +	}
 16.1784 +
 16.1785 +	memcpy(dest, source, len);
 16.1786 +	dest[len] = '\0';
 16.1787 +
 16.1788 +	return SP_ERROR_NONE;
 16.1789 +}
 16.1790 +
 16.1791 +inline int __CheckValidChar(char *c)
 16.1792 +{
 16.1793 +	int count;
 16.1794 +	int bytecount = 0;
 16.1795 +
 16.1796 +	for (count=1; (*c & 0xC0) == 0x80; count++)
 16.1797 +	{
 16.1798 +		c--;
 16.1799 +	}
 16.1800 +
 16.1801 +	switch (*c & 0xF0)
 16.1802 +	{
 16.1803 +	case 0xC0:
 16.1804 +	case 0xD0:
 16.1805 +		{
 16.1806 +			bytecount = 2;
 16.1807 +			break;
 16.1808 +		}
 16.1809 +	case 0xE0:
 16.1810 +		{
 16.1811 +			bytecount = 3;
 16.1812 +			break;
 16.1813 +		}
 16.1814 +	case 0xF0:
 16.1815 +		{
 16.1816 +			bytecount = 4;
 16.1817 +			break;
 16.1818 +		}
 16.1819 +	}
 16.1820 +
 16.1821 +	if (bytecount != count)
 16.1822 +	{
 16.1823 +		return count;
 16.1824 +	}
 16.1825 +
 16.1826 +	return 0;
 16.1827 +}
 16.1828 +
 16.1829 +int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
 16.1830 +{
 16.1831 +	char *dest;
 16.1832 +	size_t len;
 16.1833 +	bool needtocheck = false;
 16.1834 +
 16.1835 +	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
 16.1836 +	{
 16.1837 +		return SP_ERROR_INVALID_ADDRESS;
 16.1838 +	}
 16.1839 +	
 16.1840 +	if (maxbytes == 0)
 16.1841 +	{
 16.1842 +		return SP_ERROR_NONE;
 16.1843 +	}
 16.1844 +
 16.1845 +	len = strlen(source);
 16.1846 +	dest = (char *)(ctx->memory + local_addr);
 16.1847 +
 16.1848 +	if ((size_t)len >= maxbytes)
 16.1849 +	{
 16.1850 +		len = maxbytes - 1;
 16.1851 +		needtocheck = true;
 16.1852 +	}
 16.1853 +
 16.1854 +	memcpy(dest, source, len);
 16.1855 +	if ((dest[len-1] & 1<<7) && needtocheck)
 16.1856 +	{
 16.1857 +		len -= __CheckValidChar(dest+len-1);
 16.1858 +	}
 16.1859 +	dest[len] = '\0';
 16.1860 +
 16.1861 +	if (wrtnbytes)
 16.1862 +	{
 16.1863 +		*wrtnbytes = len;
 16.1864 +	}
 16.1865 +
 16.1866 +	return SP_ERROR_NONE;
 16.1867 +}
 16.1868 +
 16.1869 +#define USHR(x) ((unsigned int)(x)>>1)
 16.1870 +
 16.1871 +int BaseContext::LookupFile(ucell_t addr, const char **filename)
 16.1872 +{
 16.1873 +	int high, low, mid;
 16.1874 +
 16.1875 +	high = ctx->plugin->debug.files_num;
 16.1876 +	low = -1;
 16.1877 +
 16.1878 +	while (high - low > 1)
 16.1879 +	{
 16.1880 +		mid = USHR(low + high);
 16.1881 +		if (ctx->files[mid].addr <= addr)
 16.1882 +		{
 16.1883 +			low = mid;
 16.1884 +		} else {
 16.1885 +			high = mid;
 16.1886 +		}
 16.1887 +	}
 16.1888 +
 16.1889 +	if (low == -1)
 16.1890 +	{
 16.1891 +		return SP_ERROR_NOT_FOUND;
 16.1892 +	}
 16.1893 +
 16.1894 +	*filename = ctx->files[low].name;
 16.1895 +
 16.1896 +	return SP_ERROR_NONE;
 16.1897 +}
 16.1898 +
 16.1899 +int BaseContext::LookupFunction(ucell_t addr, const char **name)
 16.1900 +{
 16.1901 +	uint32_t iter, max = ctx->plugin->debug.syms_num;
 16.1902 +
 16.1903 +	for (iter=0; iter<max; iter++)
 16.1904 +	{
 16.1905 +		if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) 
 16.1906 +			&& (ctx->symbols[iter].codestart <= addr) 
 16.1907 +			&& (ctx->symbols[iter].codeend > addr))
 16.1908 +		{
 16.1909 +			break;
 16.1910 +		}
 16.1911 +	}
 16.1912 +
 16.1913 +	if (iter >= max)
 16.1914 +	{
 16.1915 +		return SP_ERROR_NOT_FOUND;
 16.1916 +	}
 16.1917 +
 16.1918 +	*name = ctx->symbols[iter].name;
 16.1919 +
 16.1920 +	return SP_ERROR_NONE;
 16.1921 +}
 16.1922 +
 16.1923 +int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
 16.1924 +{
 16.1925 +	int high, low, mid;
 16.1926 +
 16.1927 +	high = ctx->plugin->debug.lines_num;
 16.1928 +	low = -1;
 16.1929 +
 16.1930 +	while (high - low > 1)
 16.1931 +	{
 16.1932 +		mid = USHR(low + high);
 16.1933 +		if (ctx->lines[mid].addr <= addr)
 16.1934 +		{
 16.1935 +			low = mid;
 16.1936 +		} else {
 16.1937 +			high = mid;
 16.1938 +		}
 16.1939 +	}
 16.1940 +
 16.1941 +	if (low == -1)
 16.1942 +	{
 16.1943 +		return SP_ERROR_NOT_FOUND;
 16.1944 +	}
 16.1945 +
 16.1946 +	/* Since the CIP occurs BEFORE the line, we have to add one */
 16.1947 +	*line = ctx->lines[low].line + 1;
 16.1948 +
 16.1949 +	return SP_ERROR_NONE;
 16.1950 +}
 16.1951 +
 16.1952 +IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
 16.1953 +{
 16.1954 +	CFunction *pFunc = NULL;
 16.1955 +
 16.1956 +	if (func_id & 1)
 16.1957 +	{
 16.1958 +		func_id >>= 1;
 16.1959 +		if (func_id >= ctx->plugin->info.publics_num)
 16.1960 +		{
 16.1961 +			return NULL;
 16.1962 +		}
 16.1963 +		pFunc = m_pub_funcs[func_id];
 16.1964 +		if (!pFunc)
 16.1965 +		{
 16.1966 +			m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
 16.1967 +			pFunc = m_pub_funcs[func_id];
 16.1968 +		} else if (pFunc->IsInvalidated()) {
 16.1969 +			pFunc->Set(ctx->publics[func_id].code_offs, this);
 16.1970 +		}
 16.1971 +	} else {
 16.1972 +		/* :TODO: currently not used */
 16.1973 +#if 0
 16.1974 +		func_id >>= 1;
 16.1975 +		unsigned int index;
 16.1976 +		if (!g_pVM->FunctionLookup(ctx, func_id, &index))
 16.1977 +		{
 16.1978 +			return NULL;
 16.1979 +		}
 16.1980 +		pFunc = m_priv_funcs[func_id];
 16.1981 +		if (!pFunc)
 16.1982 +		{
 16.1983 +			m_priv_funcs[func_id] = new CFunction(save, this);
 16.1984 +			pFunc = m_priv_funcs[func_id];
 16.1985 +		}
 16.1986 +#endif
 16.1987 +		assert(false);
 16.1988 +	}
 16.1989 +
 16.1990 +	return pFunc;
 16.1991 +}
 16.1992 +
 16.1993 +IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
 16.1994 +{
 16.1995 +	uint32_t index;
 16.1996 +
 16.1997 +	if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
 16.1998 +	{
 16.1999 +		return NULL;
 16.2000 +	}
 16.2001 +
 16.2002 +	CFunction *pFunc = m_pub_funcs[index];
 16.2003 +	if (!pFunc)
 16.2004 +	{
 16.2005 +		sp_public_t *pub = NULL;
 16.2006 +		GetPublicByIndex(index, &pub);
 16.2007 +		if (pub)
 16.2008 +		{
 16.2009 +			m_pub_funcs[index] = new CFunction(pub->code_offs, this);
 16.2010 +		}
 16.2011 +		pFunc = m_pub_funcs[index];
 16.2012 +	} else if (pFunc->IsInvalidated()) {
 16.2013 +		sp_public_t *pub = NULL;
 16.2014 +		GetPublicByIndex(index, &pub);
 16.2015 +		if (pub)
 16.2016 +		{
 16.2017 +			pFunc->Set(pub->code_offs, this);
 16.2018 +		} else {
 16.2019 +			pFunc = NULL;
 16.2020 +		}
 16.2021 +	}
 16.2022 +
 16.2023 +	return pFunc;
 16.2024 +}
 16.2025 +
 16.2026 +int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
 16.2027 +{
 16.2028 +	int err;
 16.2029 +	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
 16.2030 +	{
 16.2031 +		return err;
 16.2032 +	}
 16.2033 +
 16.2034 +	if ((cell_t *)*addr == m_pNullString)
 16.2035 +	{
 16.2036 +		*addr = NULL;
 16.2037 +	}
 16.2038 +
 16.2039 +	return SP_ERROR_NONE;
 16.2040 +}
 16.2041 +
 16.2042 +#if defined SOURCEMOD_BUILD
 16.2043 +SourceMod::IdentityToken_t *BaseContext::GetIdentity()
 16.2044 +{
 16.2045 +	return m_pToken;
 16.2046 +}
 16.2047 +
 16.2048 +void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
 16.2049 +{
 16.2050 +	m_pToken = token;
 16.2051 +}
 16.2052 +
 16.2053 +cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
 16.2054 +{
 16.2055 +	if (type == SP_NULL_VECTOR)
 16.2056 +	{
 16.2057 +		return m_pNullVec;
 16.2058 +	}
 16.2059 +
 16.2060 +	return NULL;
 16.2061 +}
 16.2062 +#endif
    17.1 --- a/core/vm/sp_vm_engine.cpp	Tue Aug 07 22:11:00 2007 +0000
    17.2 +++ b/core/vm/sp_vm_engine.cpp	Wed Aug 08 02:27:10 2007 +0000
    17.3 @@ -81,6 +81,16 @@
    17.4  	"Call was aborted",
    17.5  };
    17.6  
    17.7 +const char *GetSourcePawnErrorMessage(int error)
    17.8 +{
    17.9 +	if (error < 1 || error > ERROR_MESSAGE_MAX)
   17.10 +	{
   17.11 +		return NULL;
   17.12 +	}
   17.13 +
   17.14 +	return g_ErrorMsgTable[error];
   17.15 +}
   17.16 +
   17.17  SourcePawnEngine::SourcePawnEngine()
   17.18  {
   17.19  	m_pDebugHook = NULL;
    18.1 --- a/core/vm/sp_vm_engine.h	Tue Aug 07 22:11:00 2007 +0000
    18.2 +++ b/core/vm/sp_vm_engine.h	Wed Aug 08 02:27:10 2007 +0000
    18.3 @@ -45,7 +45,6 @@
    18.4  	unsigned int chain;
    18.5  };
    18.6  
    18.7 -
    18.8  class CContextTrace : public IContextTrace
    18.9  {
   18.10  public:
    19.1 --- a/plugins/include/menus.inc	Tue Aug 07 22:11:00 2007 +0000
    19.2 +++ b/plugins/include/menus.inc	Wed Aug 08 02:27:10 2007 +0000
    19.3 @@ -68,6 +68,11 @@
    19.4  #define MENUFLAG_BUTTON_EXITBACK	(1<<1)	/**< Menu has an "exit back" button */
    19.5  #define MENUFLAG_NO_SOUND			(1<<2)	/**< Menu will not have any select sounds */
    19.6  
    19.7 +#define VOTEINFO_CLIENT_INDEX		0		/**< Client index */
    19.8 +#define VOTEINFO_CLIENT_ITEM		1		/**< Item the client selected, or -1 for none */
    19.9 +#define VOTEINFO_ITEM_INDEX			0		/**< Item index */
   19.10 +#define VOTEINFO_ITEM_VOTES			1		/**< Number of votes for the item */
   19.11 +
   19.12  /**
   19.13   * Reasons a menu can be cancelled.
   19.14   */
   19.15 @@ -351,12 +356,20 @@
   19.16  native SetMenuOptionFlags(Handle:menu, flags);
   19.17  
   19.18  /**
   19.19 - * Returns whether a vote is in progress on the given menu.
   19.20 + * Returns whether a vote is in progress.
   19.21   *
   19.22 - * @param menu				Menu Handle.
   19.23 + * @param menu				Deprecated; no longer used.
   19.24   * @return					True if a vote is in progress, false otherwise.
   19.25   */
   19.26 -native bool:IsVoteInProgress(Handle:menu);
   19.27 +native bool:IsVoteInProgress(Handle:menu=INVALID_HANDLE);
   19.28 +
   19.29 +/**
   19.30 + * Cancels the vote in progress.
   19.31 + *
   19.32 + * @noreturn
   19.33 + * @error					If no vote is in progress.
   19.34 + */
   19.35 +native CancelVote();
   19.36  
   19.37  /**
   19.38   * Broadcasts a menu to a list of clients.  The most selected item will be 
   19.39 @@ -372,7 +385,7 @@
   19.40   * @param time				Maximum time to leave menu on the screen.
   19.41   * @return					True on success, false if this menu already has a vote session
   19.42   *							in progress.
   19.43 - * @error					Invalid Handle.
   19.44 + * @error					Invalid Handle, or a vote is already in progress.
   19.45   */
   19.46  native bool:VoteMenu(Handle:menu, clients[], numClients, time);
   19.47  
   19.48 @@ -402,6 +415,33 @@
   19.49  	
   19.50  	return VoteMenu(menu, players, total, time);
   19.51  }
   19.52 +/**
   19.53 + * Callback for when a vote has ended and results are available.
   19.54 + *
   19.55 + * @param num_votes			Number of votes tallied in total.
   19.56 + * @param num_clients		Number of clients who could vote.
   19.57 + * @param client_info		Array of clients.  Use VOTEINFO_CLIENT_ defines.
   19.58 + * @param num_items			Number of unique items that were selected.
   19.59 + * @param item_info			Array of items, sorted by count.  Use VOTEINFO_ITEM
   19.60 + *							defines.
   19.61 + * @noreturn
   19.62 + */
   19.63 +functag VoteHandler public(num_votes, 
   19.64 +						   num_clients,
   19.65 +						   const client_info[][2], 
   19.66 +						   num_items,
   19.67 +						   const item_info[][2]);	   
   19.68 +
   19.69 +/**
   19.70 + * Sets an advanced vote handling callback.  If this callback is set,
   19.71 + * MenuAction_VoteEnd will not be called.
   19.72 + *
   19.73 + * @param menu				Menu Handle.
   19.74 + * @param callback			Callback function.
   19.75 + * @noreturn
   19.76 + * @error					Invalid Handle or callback.
   19.77 + */
   19.78 +native SetVoteResultCallback(Handle:menu, VoteHandler:callback);
   19.79  
   19.80  /**
   19.81   * Returns a style's global Handle.
    20.1 --- a/public/IMenuManager.h	Tue Aug 07 22:11:00 2007 +0000
    20.2 +++ b/public/IMenuManager.h	Wed Aug 08 02:27:10 2007 +0000
    20.3 @@ -37,7 +37,7 @@
    20.4  #include <IHandleSys.h>
    20.5  
    20.6  #define SMINTERFACE_MENUMANAGER_NAME		"IMenuManager"
    20.7 -#define SMINTERFACE_MENUMANAGER_VERSION		8
    20.8 +#define SMINTERFACE_MENUMANAGER_VERSION		9
    20.9  
   20.10  /**
   20.11   * @file IMenuManager.h
   20.12 @@ -118,6 +118,27 @@
   20.13  	};
   20.14  
   20.15  	/**
   20.16 +	 * @brief Contains information about a vote result.
   20.17 +	 */
   20.18 +	struct menu_vote_result_t
   20.19 +	{
   20.20 +		unsigned int num_clients;		/**< Number of clients the menu was displayed to */
   20.21 +		unsigned int num_votes;			/**< Number of votes received */
   20.22 +		struct menu_client_vote_t
   20.23 +		{
   20.24 +			int client;					/**< Client index */
   20.25 +			int item;					/**< Item # (or -1 for none) */
   20.26 +		} *client_list;					/**< Array of size num_clients */
   20.27 +		unsigned int num_items;			/**< Number of items voted for */
   20.28 +		struct menu_item_vote_t
   20.29 +		{
   20.30 +			unsigned int item;			/**< Item index */
   20.31 +			unsigned int count;			/**< Number of votes */
   20.32 +		} *item_list;					/**< Array of size num_items, sorted by count, 
   20.33 +											 descending */
   20.34 +	};
   20.35 +
   20.36 +	/**
   20.37  	 * @brief Reasons for a menu dying.
   20.38  	 */
   20.39  	enum MenuCancelReason
   20.40 @@ -141,6 +162,7 @@
   20.41  		MenuEnd_Cancelled = -3,				/**< Menu was uncleanly cancelled */
   20.42  		MenuEnd_Exit = -4,					/**< Menu was cleanly exited via "exit" */
   20.43  		MenuEnd_ExitBack = -5,				/**< Menu was cleanly exited via "back" */
   20.44 +		MenuEnd_NoVotes = -6,				/**< No votes received */
   20.45  	};
   20.46  
   20.47  
   20.48 @@ -479,31 +501,14 @@
   20.49  		virtual IMenuPanel *CreatePanel() =0;
   20.50  
   20.51  		/**
   20.52 -		 * @brief Returns whether or not the menu should have an "Exit" button for
   20.53 -		 * paginated menus.
   20.54 -		 *
   20.55 -		 * @return				True to have an exit button, false otherwise.
   20.56 -		 */
   20.57 -		virtual bool GetExitButton() =0;
   20.58 -		
   20.59 -		/**
   20.60 -		 * @brief Sets whether or not the menu should have an "Exit" button for
   20.61 -		 * paginated menus.
   20.62 -		 *
   20.63 -		 * @param set			True to enable, false to disable the exit button.
   20.64 -		 * @return				True on success, false if the exit button is 
   20.65 -		 *						non-optional.
   20.66 -		 */
   20.67 -		virtual bool SetExitButton(bool set) =0;
   20.68 -
   20.69 -		/**
   20.70  		 * @brief Sends the menu to a client.
   20.71  		 *
   20.72  		 * @param client		Client index to display to.
   20.73  		 * @param time			Time to hold menu for.
   20.74 +		 * @param alt_handler	Alternate IMenuHandler.
   20.75  		 * @return				True on success, false otherwise.
   20.76  		 */
   20.77 -		virtual bool Display(int client, unsigned int time) =0;
   20.78 +		virtual bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL) =0;
   20.79  
   20.80  		/**
   20.81  		 * @brief Destroys the menu and frees all associated resources.
   20.82 @@ -531,46 +536,6 @@
   20.83  		 * @return				Handle_t handle value.
   20.84  		 */
   20.85  		virtual Handle_t GetHandle() =0;
   20.86 -
   20.87 -		/**
   20.88 -		 * @brief Sends a menu to multiple clients as a vote menu.  All callbacks
   20.89 -		 * will be sent as normal, except two extras, OnMenuVoteStart and 
   20.90 -		 * OnMenuVoteEnd, will be called.
   20.91 -		 *
   20.92 -		 * @param clients		Array of client indexes.
   20.93 -		 * @param numClients	Number of client indexes in the array.
   20.94 -		 * @param maxTime		Maximum amount of time to hold the vote.
   20.95 -		 * @param flags			Optional voting flags (currently unused).
   20.96 -		 * @return				True on success, false if a vote is already in
   20.97 -		 *						progress (the menu must be cancelled first).
   20.98 -		 */
   20.99 -		virtual bool BroadcastVote(int clients[], 
  20.100 -			unsigned int numClients, 
  20.101 -			unsigned int maxTime,
  20.102 -			unsigned int flags=0) =0;
  20.103 -
  20.104 -		/**
  20.105 -		 * @brief Returns whether a vote menu is active.
  20.106 -		 *
  20.107 -		 * @return				True if a vote menu is active, false otherwise.
  20.108 -		 */
  20.109 -		virtual bool IsVoteInProgress() =0;
  20.110 -
  20.111 -		/**
  20.112 -		 * @brief Returns whether to draw a "Back" button on the first page.
  20.113 -		 * ExitBack buttons are disabled by default.
  20.114 -		 *
  20.115 -		 * @return				True if enabled, false otherwise.
  20.116 -		 */
  20.117 -		virtual bool GetExitBackButton() =0;
  20.118 -
  20.119 -		/**
  20.120 -		 * @brief Sets whether to draw a "Back" button on the first page.
  20.121 -		 * ExitBack buttons are disabled by default.
  20.122 -		 *
  20.123 -		 * @param set			True to enable, false to disable.
  20.124 -		 */
  20.125 -		virtual void SetExitBackButton(bool set) =0;
  20.126  		
  20.127  		/**
  20.128  		 * @brief Returns menu option flags.
  20.129 @@ -585,6 +550,13 @@
  20.130  		 * @param flags			Menu option flags.
  20.131  		 */
  20.132  		virtual void SetMenuOptionFlags(unsigned int flags) =0;
  20.133 +
  20.134 +		/**
  20.135 +		 * @brief Returns the menu's handler.
  20.136 +		 *
  20.137 +		 * @return				IMenuHandler of the menu.
  20.138 +		 */
  20.139 +		virtual IMenuHandler *GetHandler() =0;
  20.140  	};
  20.141  
  20.142  	/** 
  20.143 @@ -716,20 +688,15 @@
  20.144  		 * while it is in this function.
  20.145  		 *
  20.146  		 * @param menu			Menu pointer.
  20.147 -		 * @param item			Item position that was chosen by a majority.
  20.148 -		 * @param winningVotes	Number of votes from the winning item.
  20.149 -		 * @param totalVotes	Number of votes total.
  20.150 +		 * @param results		Menu vote results.
  20.151  		 */
  20.152 -		virtual void OnMenuVoteEnd(IBaseMenu *menu,
  20.153 -			unsigned int item, 
  20.154 -			unsigned int winningVotes,
  20.155 -			unsigned int totalVotes)
  20.156 +		virtual void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
  20.157  		{
  20.158  		}
  20.159  
  20.160  		/**
  20.161  		 * @brief Called when a vote is cancelled.  If this is called, then 
  20.162 -		 * OnMenuVoteEnd() will not be called.  In both cases, OnMenuEnd will 
  20.163 +		 * OnMenuVoteResults() will not be called.  In both cases, OnMenuEnd will 
  20.164  		 * always be called.
  20.165  		 *
  20.166  		 * @param menu			Menu pointer.
  20.167 @@ -737,43 +704,18 @@
  20.168  		virtual void OnMenuVoteCancel(IBaseMenu *menu)
  20.169  		{
  20.170  		}
  20.171 -	};
  20.172 -
  20.173 -	/**
  20.174 -	 * @brief Contains functions for managing a vote handler.
  20.175 -	 */
  20.176 -	class IVoteMenuHandler : public IMenuHandler
  20.177 -	{
  20.178 -	public:
  20.179 -		/**
  20.180 -		 * @brief Returns whether or not a vote is in progress.
  20.181 -		 *
  20.182 -		 * @return				True if a vote is in progress, false otherwise.
  20.183 -		 */
  20.184 -		virtual bool IsVoteInProgress() =0;
  20.185  
  20.186  		/**
  20.187 -		 * @brief Use this to mark the vote as in progress (start).
  20.188 +		 * @brief Call to set private handler stuff.
  20.189  		 *
  20.190 -		 * @param menu			Menu pointer.
  20.191 -		 */
  20.192 -		virtual void InitializeVoting(IBaseMenu *menu) =0;
  20.193 -
  20.194 -		/**
  20.195 -		 * @brief Use this to notify that all clients' displays have been
  20.196 -		 * processed (i.e., there are no more clients to display to).
  20.197 +		 * @param option		Option name.
  20.198 +		 * @param data			Private data.
  20.199 +		 * @return				True if set, false if invalid or unrecognized.
  20.200  		 */
  20.201 -		virtual void StartVoting() =0;
  20.202 -
  20.203 -		/**
  20.204 -		 * @brief Notifies the vote handler that the voting should be 
  20.205 -		 * cancelled.  
  20.206 -		 *
  20.207 -		 * Cancellation is not immediate and will only occur once every menu 
  20.208 -		 * has been cancelled from clients.  Thus this should only be called
  20.209 -		 * from the beginning of IBaseMenu::Cancel.
  20.210 -		 */
  20.211 -		virtual void CancelVoting() =0;
  20.212 +		virtual bool OnSetHandlerOption(const char *option, const void *data)
  20.213 +		{
  20.214 +			return false;
  20.215 +		}
  20.216  	};
  20.217  
  20.218  	/**
  20.219 @@ -792,7 +734,7 @@
  20.220  		}
  20.221  		virtual bool IsVersionCompatible(unsigned int version)
  20.222  		{
  20.223 -			if (version < 7 || version > GetInterfaceVersion())
  20.224 +			if (version < 9 || version > GetInterfaceVersion())
  20.225  			{
  20.226  				return false;
  20.227  			}
  20.228 @@ -815,7 +757,7 @@
  20.229  		virtual IMenuStyle *GetDefaultStyle() =0;
  20.230  
  20.231  		/**
  20.232 -		 * @brief Given a set of menu states, converts it to an IDisplay object.
  20.233 +		 * @brief Given a set of menu states, converts it to an IMenuPanel object.
  20.234  		 *
  20.235  		 * The state parameter is both INPUT and OUTPUT.
  20.236  		 * INPUT: menu, mh, firstItem, lastItem
  20.237 @@ -823,7 +765,7 @@
  20.238  		 *
  20.239  		 * @param client		Client index.
  20.240  		 * @param states		Menu states.
  20.241 -		 * @return				IDisplay pointer, or NULL if no items could be 
  20.242 +		 * @return				IMenuPanel pointer, or NULL if no items could be 
  20.243  		 *						found in the IBaseMenu pointer, or NULL if any
  20.244  		 *						other error occurred.  Any valid pointer must
  20.245  		 *						be freed using IMenuPanel::DeleteThis.
  20.246 @@ -831,25 +773,40 @@
  20.247  		virtual IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order) =0;
  20.248  
  20.249  		/**
  20.250 -		 * @brief Creates a standard voting wrapper.  The wrapper is not 
  20.251 -		 * re-entrant; a second menu cannot be displayed on the same handler
  20.252 -		 * at the same time.
  20.253 +		 * @brief Cancels a menu.  Calls IBaseMenu::Cancel() after doing some preparatory 
  20.254 +		 * work.  This should always be used instead of directly calling Cancel().
  20.255  		 *
  20.256 -		 * @param mh			Menu handler to wrap around.
  20.257 -		 * @return				An IMenuHandler pointer that is a wrapper
  20.258 -		 *						around IMenuHandler callbacks to invoke
  20.259 -		 *						voting related callbacks.
  20.260 +		 * @param menu			IBaseMenu pointer.
  20.261  		 */
  20.262 -		virtual IVoteMenuHandler *CreateVoteWrapper(IMenuHandler *mh) =0;
  20.263 +		virtual void CancelMenu(IBaseMenu *menu) =0;
  20.264  
  20.265  		/**
  20.266 -		 * @brief Frees a standard voting wrapper.
  20.267 +		 * @brief Displays a menu as a vote.
  20.268  		 *
  20.269 -		 * @param mh			Menu handler pointer created by 
  20.270 -		 *						CreateVoteWrapper().  NULL values will be 
  20.271 -		 *						safely ignored.
  20.272 +		 * @param menu			IBaseMenu pointer.
  20.273 +		 * @param num_clients	Number of clients to display to.
  20.274 +		 * @param clients		Client index array.
  20.275 +		 * @param max_time		Maximum time to hold menu for.
  20.276 +		 * @param flags			Vote flags (currently unused).
  20.277 +		 * @return				True on success, false if a vote is in progress.
  20.278  		 */
  20.279 -		virtual void ReleaseVoteWrapper(IVoteMenuHandler *mh) =0;
  20.280 +		virtual bool StartVote(IBaseMenu *menu,
  20.281 +								unsigned int num_clients,
  20.282 +								int clients[],
  20.283 +								unsigned int max_time,
  20.284 +								unsigned int flags=0) =0;
  20.285 +
  20.286 +		/**
  20.287 +		 * @brief Returns whether or not a vote is in progress.
  20.288 +		 *
  20.289 +		 * @return				True if a vote is in progress, false otherwise.
  20.290 +		 */
  20.291 +		virtual bool IsVoteInProgress() =0;
  20.292 +
  20.293 +		/**
  20.294 +		 * @brief Cancels the vote in progress.  This calls IBaseMenu::Cancel().
  20.295 +		 */
  20.296 +		virtual void CancelVoting() =0;
  20.297  	};
  20.298  }
  20.299