1 /////////////////////////////////////////
2 //
3 //             OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10 
11 
12 // Menu System functions
13 // Created 30/6/02
14 // Jason Boettcher
15 
16 
17 #include <assert.h>
18 
19 #include <set>
20 
21 #include "Debug.h"
22 #include "LieroX.h"
23 #include "console.h"
24 #include "EndianSwap.h"
25 #include "AuxLib.h"
26 #include "Error.h"
27 #include "ConfigHandler.h"
28 #include "CClient.h"
29 #include "IpToCountryDB.h"
30 #include "DeprecatedGUI/Graphics.h"
31 #include "DeprecatedGUI/Menu.h"
32 #include "GfxPrimitives.h"
33 #include "FindFile.h"
34 #include "StringUtils.h"
35 #include "CWorm.h"
36 #include "Cursor.h"
37 #include "DeprecatedGUI/CButton.h"
38 #include "DedicatedControl.h"
39 #include "OLXG15.h"
40 #include "Timer.h"
41 #include "IRC.h"
42 #include "FileUtils.h"
43 #include "Command.h"
44 #include "HTTP.h"
45 #include "Version.h"
46 #include "CrashHandler.h"
47 #include "Touchscreen.h"
48 
49 
50 // TODO: move this out here
51 // declare them only locally here as nobody really should use them explicitly
52 std::string Utf8String(const std::string &OldLxString);
53 
54 
55 
56 
57 namespace DeprecatedGUI {
58 
59 menu_t	*tMenu = NULL;
60 
61 
62 bool		*bGame = NULL;
63 int			iSkipStart = false;
64 CWidgetList	LayoutWidgets[LAYOUT_COUNT];
65 
66 static int Menu_MouseWarpedX = 0;
67 static int Menu_MouseWarpedY = 0;
68 
69 ///////////////////
70 // Initialize the menu system
Menu_Initialize(bool * game)71 bool Menu_Initialize(bool *game)
72 {
73 	bGame = game;
74 	*bGame = false;
75 	bJoin_Update = true;
76 	bHost_Update = true;
77 
78 	// Allocate the menu structure
79 	tMenu = new menu_t;
80     if(tMenu == NULL) {
81         SystemError("Error: Out of memory in for menu");
82 		return false;
83     }
84 
85 	if(bDedicated) return true;
86 
87 	// Load the frontend info
88 	Menu_LoadFrontendInfo();
89 
90 	tMenu->iReturnTo = net_internet;
91 	tMenu->bForbidConsole = false;
92 	tMenu->iListItemHeight = tLX->cFont.GetHeight();
93 	tMenu->bFingerDrag = false;
94 #ifdef __ANDROID__
95 	if (!getenv("ANDROID_TV"))
96 #else
97 	if (getenv("TEST_TOUCH_UI"))
98 #endif
99 	{
100 		tMenu->bFingerDrag = true;
101 		tMenu->iListItemHeight = tLX->cFont.GetHeight() * 2;
102 	}
103 
104 	// Load the images
105 	//LOAD_IMAGE(tMenu->bmpMainBack,"data/frontend/background.png");
106     //LOAD_IMAGE(tMenu->bmpMainBack_lg,"data/frontend/background_lg.png");
107     LOAD_IMAGE(tMenu->bmpMainBack_wob,"data/frontend/background_wob.png");
108 
109 	// bmpMainBack_common, for backward compatibility: if it doesn't exist, we use bmpMainBack_wob
110 	tMenu->bmpMainBack_common = LoadGameImage("data/frontend/background_common.png");
111 	if (!tMenu->bmpMainBack_common.get())
112 		tMenu->bmpMainBack_common = tMenu->bmpMainBack_wob;
113 
114 
115 	tMenu->bmpBuffer = gfxCreateSurface(640,480);
116     if(tMenu->bmpBuffer.get() == NULL) {
117         SystemError("Error: Out of memory back buffer");
118 		return false;
119     }
120 
121 
122 	tMenu->bmpMsgBuffer = gfxCreateSurface(640,480);
123     if(tMenu->bmpMsgBuffer.get() == NULL) {
124         SystemError("Error: Out of memory in MsgBuffer");
125 		return false;
126 	}
127 
128     tMenu->bmpMiniMapBuffer = gfxCreateSurface(128,96);
129     if(tMenu->bmpMiniMapBuffer.get() == NULL) {
130         SystemError("Error: Out of memory in MiniMapBuffer");
131 		return false;
132     }
133 
134 	SmartPointer<SDL_Surface> lobby_state = NULL;
135 	LOAD_IMAGE_WITHALPHA(tMenu->bmpMainTitles,"data/frontend/maintitles.png");
136 	LOAD_IMAGE_WITHALPHA(tMenu->bmpLieroXtreme,"data/frontend/lierox.png");
137 	LOAD_IMAGE_WITHALPHA(tMenu->bmpSubTitles,"data/frontend/subtitles.png");
138 	LOAD_IMAGE_WITHALPHA(tMenu->bmpButtons,"data/frontend/buttons.png");
139 	LOAD_IMAGE_WITHALPHA(tMenu->bmpMapEdTool,"data/frontend/map_toolbar.png");
140 	LOAD_IMAGE_WITHALPHA(tMenu->bmpCheckbox,"data/frontend/checkbox.png");
141 	LOAD_IMAGE_WITHALPHA(tMenu->bmpInputbox,"data/frontend/inputbox.png");
142 	//LOAD_IMAGE_WITHALPHA(tMenu->bmpAI,"data/frontend/cpu.png");
143 	LOAD_IMAGE_WITHALPHA(lobby_state, "data/frontend/lobbyready.png");;
144 	LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[0], "data/frontend/con_good.png");
145 	LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[1], "data/frontend/con_average.png");
146 	LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[2], "data/frontend/con_bad.png");
147 	LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[3], "data/frontend/con_none.png");
148 	LOAD_IMAGE_WITHALPHA2(tMenu->bmpConnectionSpeeds[4], "data/frontend/con_nat.png", "data/frontend/con_bad.png");
149 	LOAD_IMAGE_WITHALPHA(tMenu->bmpTriangleUp, "data/frontend/triangle_up.png");
150 	LOAD_IMAGE_WITHALPHA(tMenu->bmpTriangleDown, "data/frontend/triangle_down.png");
151 	tMenu->bmpDownload = LoadGameImage("data/frontend/download.png", true); // Doesn't have to exist
152 	tMenu->bmpChatBackground = LoadGameImage("data/frontend/background_chat.png", true);
153 	tMenu->bmpChatBackgroundMain = LoadGameImage("data/frontend/background_chat_main.png", true);
154 
155 	// Split up the lobby ready image
156 	tMenu->bmpLobbyReady = gfxCreateSurfaceAlpha(lobby_state.get()->w, 12);
157 	if (!tMenu->bmpLobbyReady.get())  {
158 		errors << "Out of memory while creating tMenu->bmpLobbyReady" << endl;
159 		return false;
160 	}
161 	CopySurface(tMenu->bmpLobbyReady.get(), lobby_state, 0, 0, 0, 0, lobby_state.get()->w, 12);
162 
163 	tMenu->bmpLobbyNotReady = gfxCreateSurfaceAlpha(lobby_state.get()->w, 12);
164 	if (!tMenu->bmpLobbyNotReady.get())  {
165 		errors << "Out of memory while creating tMenu->bmpLobbyNotReady" << endl;
166 		return false;
167 	}
168 	CopySurface(tMenu->bmpLobbyNotReady.get(), lobby_state, 0, 12, 0, 0, lobby_state.get()->w, 12);
169 
170 
171 	for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
172 		tMenu->tSocket[i] = new NetworkSocket();
173 
174 	// HACK: open an unreliable foo socket
175 	// Some routers simply ignore first open socket and don't let any data through, this is a workaround
176 	tMenu->tSocket[SCK_FOO]->OpenUnreliable(0);
177 	// Open a socket for broadcasting over a LAN (UDP)
178 	tMenu->tSocket[SCK_LAN]->OpenBroadcast(0);
179 	// Open a socket for communicating over the net (UDP)
180 	tMenu->tSocket[SCK_NET]->OpenUnreliable(0);
181 
182 	if(!tMenu->tSocket[SCK_LAN]->isOpen() || !tMenu->tSocket[SCK_NET]->isOpen()) {
183 		SystemError("Error: Failed to open a socket for networking");
184 		return false;
185 	}
186 
187 	// Send some random data to some random IP
188 	if (tMenu->tSocket[SCK_FOO]->isOpen())  {
189 		NetworkAddr a; StringToNetAddr("1.2.3.4:5678", a);
190 		// For example, if no network is connected, you likely only have 127.* in your routing table.
191 		if(IsNetAddrAvailable(a)) {
192 			tMenu->tSocket[SCK_FOO]->setRemoteAddress(a);
193 			tMenu->tSocket[SCK_FOO]->Write("foo");
194 		}
195 	}
196 
197 	// Add default widget IDs to the widget list
198 	//Menu_AddDefaultWidgets();
199 
200 	return true;
201 }
202 
203 /////////////////////////
204 // Load the infor about frontend
Menu_LoadFrontendInfo()205 void Menu_LoadFrontendInfo()
206 {
207 	ReadInteger("data/frontend/frontend.cfg","MainTitles","X",&tMenu->tFrontendInfo.iMainTitlesLeft,50);
208 	ReadInteger("data/frontend/frontend.cfg","MainTitles","Y",&tMenu->tFrontendInfo.iMainTitlesTop,160);
209 	ReadInteger("data/frontend/frontend.cfg","Credits","X",&tMenu->tFrontendInfo.iCreditsLeft,370);
210 	ReadInteger("data/frontend/frontend.cfg","Credits","Y",&tMenu->tFrontendInfo.iCreditsTop,379);
211 	ReadInteger("data/frontend/frontend.cfg","Credits","Spacing",&tMenu->tFrontendInfo.iCreditsSpacing,0);
212 	ReadString ("data/frontend/frontend.cfg","Credits","FrontendCredits", tMenu->tFrontendInfo.sFrontendCredits, " ");
213 	ReadInteger("data/frontend/frontend.cfg","MainTitles","Spacing",&tMenu->tFrontendInfo.iMainTitlesSpacing,15);
214 	ReadKeyword("data/frontend/frontend.cfg","PageBoxes","Visible",&tMenu->tFrontendInfo.bPageBoxes,true);
215 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","AnimX",&tMenu->tFrontendInfo.iLoadingAnimLeft,5);
216 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","AnimY",&tMenu->tFrontendInfo.iLoadingAnimTop,5);
217 	ReadFloat("data/frontend/frontend.cfg","IpToCountryLoading","AnimFrameTime",&tMenu->tFrontendInfo.fLoadingAnimFrameTime,0.2f);
218 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","BarX",&tMenu->tFrontendInfo.iLoadingBarLeft,5);
219 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","BarY",&tMenu->tFrontendInfo.iLoadingBarTop,80);
220 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","LabelX",&tMenu->tFrontendInfo.iLoadingLabelLeft,5);
221 	ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","LabelY",&tMenu->tFrontendInfo.iLoadingLabelTop,60);
222 }
223 
224 ///////////////////
225 // Shutdown the menu
Menu_Shutdown()226 void Menu_Shutdown()
227 {
228 	Menu_Current_Shutdown();
229 
230 	if(tMenu) {
231 		// The rest get free'd in the cache
232 		delete tMenu;
233 		tMenu = NULL;
234 	}
235 
236 	// Shutdown the layouts
237 	//for (int i=0; i<LAYOUT_COUNT; i++)
238 	//	LayoutWidgets[i].Shutdown();
239 
240 	Menu_SvrList_Shutdown();
241 }
242 
243 
244 ///////////////////
245 // Start the menu
Menu_Start()246 void Menu_Start()
247 {
248 	tMenu->bMenuRunning = true;
249 
250 	if(!bDedicated) {
251 		if(!iSkipStart) {
252 			notes << "Loading main menu" << endl;
253 			tMenu->iMenuType = MNU_MAIN;
254 			Menu_MainInitialize();
255 		} else
256 			Menu_RedrawMouse(true);
257 	}
258 
259 	iSkipStart = false;
260 	Menu_Loop();
261 }
262 
263 
264 ///////////////////
265 // Set the skip start bit
Menu_SetSkipStart(int s)266 void Menu_SetSkipStart(int s)
267 {
268     iSkipStart = s;
269 }
270 
Menu_Frame()271 void Menu_Frame() {
272 	HandlePendingCommands();
273 
274 	if(bDedicated) {
275 		DedicatedControl::Get()->Menu_Frame();
276 		return;
277 	}
278 
279 	if(!tMenu->bMenuRunning) return; // could be already quitted
280 
281 	// Check if user pressed screenshot key
282 	if (tLX->cTakeScreenshot.isDownOnce())  {
283 		PushScreenshot("scrshots", "");
284 	}
285 
286 	Menu_RedrawMouse(true);
287 
288 #ifdef WITH_G15
289 	if (OLXG15)
290 		OLXG15->menuFrame();
291 #endif //WITH_G15
292 
293 	switch(tMenu->iMenuType) {
294 
295 		// Main
296 		case MNU_MAIN:
297 			Menu_MainFrame();
298 			break;
299 
300 		// Local
301 		case MNU_LOCAL:
302 			Menu_LocalFrame();
303 			break;
304 
305 		// News
306 		case MNU_NETWORK:
307 			Menu_NetFrame();
308 			break;
309 
310 		// Player
311 		case MNU_PLAYER:
312 			Menu_PlayerFrame();
313 			break;
314 
315 		// Map editor
316 		case MNU_MAPED:
317 			Menu_MapEdFrame(VideoPostProcessor::videoSurface(),true);
318 			break;
319 
320 		// Options
321 		case MNU_OPTIONS:
322 			Menu_OptionsFrame();
323 			break;
324 	}
325 
326 	// DEBUG: show FPS
327 #ifdef DEBUG
328 	if(tLX->fDeltaTime != TimeDiff()) {
329 		Menu_redrawBufferRect(0, 0, 100, 20);
330 		tLX->cFont.Draw(VideoPostProcessor::videoSurface(), 0, 0, tLX->clWhite, "FPS: " + itoa((int)(1.0f/tLX->fDeltaTime.seconds())));
331 	}
332 #endif
333 
334 	if (!tMenu->bForbidConsole)  {
335 		Con_Process(tLX->fDeltaTime);
336 		Con_Draw(VideoPostProcessor::videoSurface());
337 	}
338 	tMenu->bForbidConsole = false; // Reset it here, it might get recovered next frame
339 
340 	// we need to clone the screen buffer because of the current way we are drawing the menu
341 	struct CopyScreenBuffer : Action {
342 		int handle() { VideoPostProcessor::cloneBuffer(); return 0; }
343 	};
344 	doVppOperation(new CopyScreenBuffer());
345 
346 	// now do the actual flip&draw
347 	doVideoFrameInMainThread();
348 
349 	SetTouchscreenControlsShown(false);
350 }
351 
352 
353 ///////////////////
354 // Main menu loop
Menu_Loop()355 void Menu_Loop()
356 {
357 	AbsTime menuStartTime = tLX->currentTime = GetTime();
358 
359 	bool last_frame_was_because_of_an_event = false;
360 	last_frame_was_because_of_an_event = ProcessEvents();
361 
362 	while(tMenu->bMenuRunning) {
363 		AbsTime oldtime = tLX->currentTime;
364 
365 		Menu_Frame();
366 		if(!tMenu->bMenuRunning) break;
367 		CapFPS();
368 		SetCrashHandlerReturnPoint("Menu_Loop");
369 
370 		if(last_frame_was_because_of_an_event || bDedicated) {
371 			// Use ProcessEvents() here to handle other processes in queue.
372 			// There aren't probably any but it has also the effect that
373 			// we run the loop another time after an event which is sometimes
374 			// because of the current code needed. Sometimes after an event,
375 			// some new menu elements got initialised but not drawn.
376 			last_frame_was_because_of_an_event = ProcessEvents();
377 		} else {
378 			last_frame_was_because_of_an_event = WaitForNextEvent();
379 		}
380 
381 		ProcessIRC();
382 
383 		tLX->currentTime = GetTime();
384 		tLX->fDeltaTime = tLX->currentTime - oldtime;
385 		tLX->fRealDeltaTime = tLX->fDeltaTime;
386 
387 		// If we have run fine for >=5 seconds, it is probably safe & make sense
388 		// to restart the game in case of a crash.
389 		if(tLX->currentTime - menuStartTime >= TimeDiff(5.0f))
390 			CrashHandler::restartAfterCrash = true;
391 	}
392 
393 	// If we go out of the menu, it means the user has selected something.
394 	// This indicates that everything is fine, so we should restart in case of a crash.
395 	// Note that we will set this again to false later on in case the user quitted.
396 	CrashHandler::restartAfterCrash = true;
397 }
398 
399 
400 ///////////////////
401 // Redraw the rectangle under the mouse (total means a total buffer redraw)
402 // TODO: rename this function (one would expect that it redraws the mouse)
Menu_RedrawMouse(bool total)403 void Menu_RedrawMouse(bool total)
404 {
405 	if(total) {
406 		SDL_BlitSurface(tMenu->bmpBuffer.get(),NULL,VideoPostProcessor::videoSurface(),NULL);
407 		return;
408 	}
409 
410 	int hw = GetMaxCursorWidth() / 2 - 1;
411 	int hh = GetMaxCursorHeight() / 2 - 1;
412 
413 	mouse_t *m = GetMouse();
414 	DrawImageAdv(VideoPostProcessor::videoSurface(),tMenu->bmpBuffer,
415 				m->X - hw - m->deltaX,
416 				m->Y - hh - m->deltaY,
417 
418 				m->X - hw - m->deltaX,
419 				m->Y - hh - m->deltaY,
420 				GetMaxCursorWidth() * 2, GetMaxCursorHeight() * 2);
421 }
422 
423 
424 ///////////////////
425 // Draw a sub title
Menu_DrawSubTitle(SDL_Surface * bmpDest,int id)426 void Menu_DrawSubTitle(SDL_Surface * bmpDest, int id)
427 {
428 	int x = VideoPostProcessor::videoSurface()->w/2;
429 	x -= tMenu->bmpSubTitles.get()->w/2;
430 
431 	DrawImageAdv(bmpDest,tMenu->bmpSubTitles, 0, id*70, x,30, tMenu->bmpSubTitles.get()->w, 65);
432 }
433 
434 
435 ///////////////////
436 // Draw a sub title advanced
Menu_DrawSubTitleAdv(SDL_Surface * bmpDest,int id,int y)437 void Menu_DrawSubTitleAdv(SDL_Surface * bmpDest, int id, int y)
438 {
439 	int x = VideoPostProcessor::videoSurface()->w/2;
440 	x -= tMenu->bmpSubTitles.get()->w/2;
441 
442 	DrawImageAdv(bmpDest,tMenu->bmpSubTitles, 0, id*70, x,y, tMenu->bmpSubTitles.get()->w, 65);
443 }
444 
445 
446 ////////////////
447 // Draws advanced box
Menu_DrawBoxAdv(SDL_Surface * bmpDest,int x,int y,int x2,int y2,int border,Color LightColour,Color DarkColour,Color BgColour,uchar type)448 void Menu_DrawBoxAdv(SDL_Surface * bmpDest, int x, int y, int x2, int y2, int border, Color LightColour, Color DarkColour, Color BgColour, uchar type)
449 {
450 	// First draw the background
451 	if (BgColour != tLX->clPink)
452 		DrawRectFill(bmpDest,x+border,y+border,x2-border+1,y2-border+1,BgColour);
453 
454 	if (!border)
455 		return;
456 
457 	int i;
458 
459 	// Switch the light and dark colour when inset
460 	if (type == BX_INSET)  {
461 		Color tmp = LightColour;
462 		LightColour = DarkColour;
463 		DarkColour = tmp;
464 	}
465 
466 	// Create gradient when needed
467 	int r_step,g_step,b_step;
468 	const Uint8 r1 = DarkColour.r, g1 = DarkColour.g, b1 = DarkColour.b;
469 	const Uint8 r2 = LightColour.r, g2 = LightColour.b, b2 = LightColour.b;
470 
471 	if (type != BX_SOLID)  {
472 		r_step = (r2-r1)/border;
473 		g_step = (g2-g1)/border;
474 		b_step = (b2-b1)/border;
475 	}
476 	else {
477 		r_step = g_step = b_step = 0;
478 	}
479 
480 
481 	// Draw the box
482 	for (i=0;i<border;i++)
483 		DrawRect(bmpDest,x+i,y+i,x2-i,y2-i,Color(r1+r_step*i,g1+g_step*i,b1+b_step*i));
484 }
485 
486 
487 ///////////////////
488 // Draw a box
Menu_DrawBox(SDL_Surface * bmpDest,int x,int y,int x2,int y2)489 void Menu_DrawBox(SDL_Surface * bmpDest, int x, int y, int x2, int y2)
490 {
491 	if(bmpDest == NULL) {
492 		errors << "Menu_DrawBox: bmpDest == NULL" << endl;
493 		return;
494 	}
495 
496 	DrawRect( bmpDest,x+1, y+1, x2-1,y2-1, tLX->clBoxLight);
497     //DrawRect( bmpDest,x+2, y+2, x2-2,y2-2, tLX->clBoxDark);
498 	DrawHLine(bmpDest,x+2, x2-1,y,  tLX->clBoxDark);
499 	DrawHLine(bmpDest,x+2, x2-1,y2, tLX->clBoxDark);
500 
501 	DrawVLine(bmpDest,y+2, y2-1,x,  tLX->clBoxDark);
502 	DrawVLine(bmpDest,y+2, y2-1,x2, tLX->clBoxDark);
503 
504     Uint32 dark = tLX->clBoxDark.get(bmpDest->format);
505 
506 	LOCK_OR_QUIT(bmpDest);
507 	PutPixel( bmpDest,x+1, y+1,     dark);
508 	PutPixel( bmpDest,x2-1,y+1,     dark);
509 	PutPixel( bmpDest,x+1, y2-1,    dark);
510 	PutPixel( bmpDest,x2-1,y2-1,    dark);
511 	UnlockSurface(bmpDest);
512 }
513 
514 
515 ///////////////////
516 // Draw an inset box
Menu_DrawBoxInset(SDL_Surface * bmpDest,int x,int y,int x2,int y2)517 void Menu_DrawBoxInset(SDL_Surface * bmpDest, int x, int y, int x2, int y2)
518 {
519 	if(bmpDest == NULL) {
520 		errors << "Menu_DrawBoxInset: bmpDest is NULL" << endl;
521 		return;
522 	}
523 
524 	// Clipping
525 	if (x < 0) { x2 += x; x = 0; }
526 	if (x2 >= bmpDest->w) { x2 = bmpDest->w - 1; }
527 	if (y < 0) { y2 += y; y = 0; }
528 	if (y2 >= bmpDest->h) { y2 = bmpDest->h - 1; }
529 
530 	DrawRect( bmpDest,x+1, y+1, x2-1,y2-1, tLX->clBoxDark);
531 	DrawHLine(bmpDest,x+2, x2-1,y,  tLX->clBoxLight);
532 	DrawHLine(bmpDest,x+2, x2-1,y2, tLX->clBoxLight);
533 
534 	DrawVLine(bmpDest,y+2, y2-1,x,  tLX->clBoxLight);
535 	DrawVLine(bmpDest,y+2, y2-1,x2, tLX->clBoxLight);
536 
537 	Uint32 light = tLX->clBoxLight.get(bmpDest->format);
538 
539 	LOCK_OR_QUIT(bmpDest);
540 	if(PointInRect(x+1,y+1,bmpDest->clip_rect)) PutPixel( bmpDest,x+1, y+1,     light);
541 	if(PointInRect(x2-1,y+1,bmpDest->clip_rect)) PutPixel( bmpDest,x2-1,y+1,     light);
542 	if(PointInRect(x+1,y2-1,bmpDest->clip_rect)) PutPixel( bmpDest,x+1, y2-1,    light);
543 	if(PointInRect(x2-1,y2-1,bmpDest->clip_rect)) PutPixel( bmpDest,x2-1,y2-1,    light);
544 	UnlockSurface(bmpDest);
545 }
546 
547 
548 ///////////////////
549 // Draw a windows style button
Menu_DrawWinButton(SDL_Surface * bmpDest,int x,int y,int w,int h,bool down)550 void Menu_DrawWinButton(SDL_Surface * bmpDest, int x, int y, int w, int h, bool down)
551 {
552     DrawRectFill(bmpDest, x,y, x+w, y+h, tLX->clWinBtnBody);
553     const Color dark = tLX->clWinBtnDark;
554     const Color light = tLX->clWinBtnLight;
555     if(down) {
556         DrawHLine(bmpDest, x, x+w, y, dark);
557         DrawHLine(bmpDest, x, x+w, y+h, light);
558         DrawVLine(bmpDest, y, y+h, x, dark);
559         DrawVLine(bmpDest, y, y+h, x+w, light);
560     } else {
561         DrawHLine(bmpDest, x, x+w, y, light);
562         DrawHLine(bmpDest, x, x+w, y+h, dark);
563         DrawVLine(bmpDest, y, y+h, x, light);
564         DrawVLine(bmpDest, y, y+h, x+w, dark);
565     }
566 }
567 
568 
569 ///////////////////
570 // Show a message box
Menu_MessageBox(const std::string & sTitle,const std::string & sText,MessageBoxType type)571 MessageBoxReturnType Menu_MessageBox(const std::string& sTitle, const std::string& sText, MessageBoxType type)
572 {
573 	SetTouchscreenControlsShown(false);
574 	if(bDedicated) {
575 		hints << "Menu_MessageBox: " << sTitle << ": " << sText << endl;
576 		switch(type) {
577 			case LMB_OK: return MBR_OK;
578 			case LMB_YESNO:
579 				hints << "Dedicated server is positive and says YES." << endl;
580 				return MBR_YES;
581 		}
582 		return MBR_OK;
583 	}
584 
585 	MessageBoxReturnType ret = MBR_INVALID;
586 	gui_event_t *ev = NULL;
587 
588 	SetGameCursor(CURSOR_ARROW);
589 
590 	int minw = 350;
591 	int maxw = 500;
592 	int x = 160;
593 	int y = 170;
594 	int w = minw; // the whole box
595 	int h = 140; // including caption and button, the whole box
596 
597 	// Adjust the width
598 	int longest_line = w;
599 	std::vector<std::string> lines = explode(sText, "\n");
600 	std::vector<std::string>::const_iterator it;
601 	for (it=lines.begin(); it!=lines.end(); it++)  {
602 		int tw = tLX->cFont.GetWidth(*it);
603 		if (tw > longest_line)
604 			longest_line = tw;
605 	}
606 	w = CLAMP(longest_line + 40, minw, maxw);
607 	x = (VideoPostProcessor::get()->screenWidth() - w) / 2;
608 
609 	// Handle multiline messages
610 	lines = splitstring(sText, (size_t)-1, w - 2, tLX->cFont);
611 
612 	const int line_hspace = 2;
613 	const int button_h = 24;
614 	const int caption_h = 25;
615 
616 	if( (tLX->cFont.GetHeight() + line_hspace) * (int)lines.size() + button_h + caption_h + 2 > h ) {
617 		// TODO: hardcoded screen height (480)
618 		h = (int)MIN( (tLX->cFont.GetHeight() + line_hspace) * lines.size() + 90, (size_t)478);
619 		y = 240-h/2;
620 	}
621 
622 	int cx = x+w/2;
623 	int cy = y + caption_h;
624 	if(lines.size() > 0) {
625 		cy += (h - button_h - caption_h) / 2;
626 		cy -= ((int)(lines.size() - 1) * (tLX->cFont.GetHeight() + line_hspace)) / 2;
627 		cy -= tLX->cFont.GetHeight() / 2;
628 	}
629 
630 	//
631 	// Setup the gui
632 	//
633 	CGuiLayout msgbox;
634 
635 	msgbox.Initialize();
636 
637 	if(type == LMB_OK)
638 		msgbox.Add( new CButton(BUT_OK,tMenu->bmpButtons), 0, cx-20,y+h-button_h, 40,15);
639 	else if(type == LMB_YESNO) {
640 		msgbox.Add( new CButton(BUT_YES,tMenu->bmpButtons), 1, x+15,y+h-button_h, 35,15);
641 		msgbox.Add( new CButton(BUT_NO,tMenu->bmpButtons),  2, x+w-35,y+h-button_h, 30,15);
642 	}
643 
644 	// Store the old buffer into a temp buffer to keep it
645 	SDL_BlitSurface(tMenu->bmpBuffer.get(), NULL, tMenu->bmpMsgBuffer.get(), NULL);
646 
647 
648 	// Draw to the buffer
649 	//DrawImage(tMenu->bmpBuffer, shadow, 177,167);
650 	Menu_DrawBox(tMenu->bmpBuffer.get(), x, y, x+w, y+h);
651 	DrawRectFill(tMenu->bmpBuffer.get(), x+2,y+2, x+w-1,y+h-1,tLX->clDialogBackground);
652 	DrawRectFill(tMenu->bmpBuffer.get(), x+2,y+2, x+w-1,y+caption_h,tLX->clDialogCaption);
653 
654 	tLX->cFont.DrawCentre(tMenu->bmpBuffer.get(), cx, y+5, tLX->clNormalLabel,sTitle);
655 	for (it=lines.begin(); it!=lines.end(); it++)  {
656 		cx = x+w/2;//-(tLX->cFont.GetWidth(lines[i])+30)/2;
657 		tLX->cFont.DrawCentre(tMenu->bmpBuffer.get(), cx, cy, tLX->clNormalLabel, *it);
658 		cy += tLX->cFont.GetHeight()+line_hspace;
659 	}
660 
661 	Menu_RedrawMouse(true);
662 
663 
664 	ProcessEvents();
665 
666 
667 	// TODO: make this event-based (don't check GetKeyboard() directly)
668 	while(true) {
669 		Menu_RedrawMouse(true);
670 
671 		SetGameCursor(CURSOR_ARROW);
672 
673 		DrawImageAdv(VideoPostProcessor::videoSurface(),tMenu->bmpBuffer, x,y, x,y, w, h);
674 
675 		// Process the gui
676 		ev = msgbox.Process();
677 		msgbox.Draw(VideoPostProcessor::videoSurface());
678 
679 		if(ev) {
680 
681 			if(ev->cWidget->getType() == wid_Button)
682 				SetGameCursor(CURSOR_HAND);
683 			if(ev->cWidget->getType() == wid_Textbox)
684 				SetGameCursor(CURSOR_TEXT);
685 
686 			if(ev->iEventMsg == BTN_CLICKED) {
687 				switch(ev->iControlID) {
688 
689 					// OK
690 					case 0:
691 						ret = MBR_OK;
692 						break;
693 
694 					// Yes
695 					case 1:
696 						ret = MBR_YES;
697 						break;
698 
699 					// No
700 					case 2:
701 						ret = MBR_NO;
702 						break;
703 				}
704 			}
705 		}
706 
707 		// Handle the Enter key
708 		if (WasKeyboardEventHappening(SDLK_RETURN) || WasKeyboardEventHappening(SDLK_KP_ENTER))
709 		{
710 			if (type == LMB_YESNO)  {
711 				ret = MBR_YES;
712 				break;
713 			}
714 			else  {
715 				ret = MBR_OK;
716 				break;
717 			}
718 		}
719 
720 		if(!WasKeyboardEventHappening(SDLK_ESCAPE) && !tLX->bQuitGame && ret == MBR_INVALID) {
721 			DrawCursor(VideoPostProcessor::videoSurface());
722 			doVideoFrameInMainThread();
723 			CapFPS();
724 			tLX->currentTime = GetTime(); // we need this for CapFPS()
725 			WaitForNextEvent();
726 		} else
727 			break;
728 	}
729 
730 	SetGameCursor(CURSOR_ARROW);
731 	msgbox.Shutdown();
732 
733 	// Restore the old buffer
734 	SDL_BlitSurface(tMenu->bmpMsgBuffer.get(), NULL, tMenu->bmpBuffer.get(), NULL);
735 	//Menu_RedrawMouse(true);
736 	//doVideoFrameInMainThread();
737 
738 	return ret;
739 }
740 
741 ///////////////////
742 // Add all the default widgets
Menu_AddDefaultWidgets()743 void Menu_AddDefaultWidgets()
744 {
745 // 34 layouts total
746 
747 // L_MAINMENU: 6 widgets
748     LayoutWidgets[L_MAINMENU].Add("LocalPlay");
749     LayoutWidgets[L_MAINMENU].Add("NetPlay");
750     LayoutWidgets[L_MAINMENU].Add("PlayerProfiles");
751     LayoutWidgets[L_MAINMENU].Add("LevelEditor");
752     LayoutWidgets[L_MAINMENU].Add("Options");
753     LayoutWidgets[L_MAINMENU].Add("Quit");
754 
755 // L_LOCALPLAY: 9 widgets
756     LayoutWidgets[L_LOCALPLAY].Add("Back");
757     LayoutWidgets[L_LOCALPLAY].Add("Start");
758     LayoutWidgets[L_LOCALPLAY].Add("Playing");
759     LayoutWidgets[L_LOCALPLAY].Add("PlayerList");
760     LayoutWidgets[L_LOCALPLAY].Add("LevelList");
761     LayoutWidgets[L_LOCALPLAY].Add("Gametype");
762     LayoutWidgets[L_LOCALPLAY].Add("ModName");
763     LayoutWidgets[L_LOCALPLAY].Add("GameSettings");
764     LayoutWidgets[L_LOCALPLAY].Add("WeaponOptions");
765 
766 // L_GAMESETTINGS: 9 widgets
767     LayoutWidgets[L_GAMESETTINGS].Add("gs_Ok");
768     LayoutWidgets[L_GAMESETTINGS].Add("gs_Default");
769     LayoutWidgets[L_GAMESETTINGS].Add("Lives");
770     LayoutWidgets[L_GAMESETTINGS].Add("MaxKills");
771     LayoutWidgets[L_GAMESETTINGS].Add("LoadingTime");
772     LayoutWidgets[L_GAMESETTINGS].Add("LoadingTimeLabel");
773     LayoutWidgets[L_GAMESETTINGS].Add("Bonuses");
774     LayoutWidgets[L_GAMESETTINGS].Add("ShowBonusNames");
775     LayoutWidgets[L_GAMESETTINGS].Add("MaxTime");
776 
777 // L_WEAPONOPTIONS: 8 widgets
778     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Ok");
779     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Scroll");
780     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Reset");
781     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_ListBox");
782     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Cancel");
783     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Random");
784     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Load");
785     LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Save");
786 
787 // L_LOADWEAPONS: 4 widgets
788     LayoutWidgets[L_LOADWEAPONS].Add("wp_Cancel");
789     LayoutWidgets[L_LOADWEAPONS].Add("wp_Ok");
790     LayoutWidgets[L_LOADWEAPONS].Add("wp_PresetList");
791     LayoutWidgets[L_LOADWEAPONS].Add("wp_PresetName");
792 
793 // L_SAVEWEAPONS: 4 widgets
794     LayoutWidgets[L_SAVEWEAPONS].Add("wp_Cancel");
795     LayoutWidgets[L_SAVEWEAPONS].Add("wp_Ok");
796     LayoutWidgets[L_SAVEWEAPONS].Add("wp_PresetList");
797     LayoutWidgets[L_SAVEWEAPONS].Add("wp_PresetName");
798 
799 // L_NET: 4 widgets
800     LayoutWidgets[L_NET].Add("InternetTab");
801     LayoutWidgets[L_NET].Add("LANTab");
802     LayoutWidgets[L_NET].Add("HostTab");
803     LayoutWidgets[L_NET].Add("FavouritesTab");
804 
805 // L_NETINTERNET: 8 widgets
806     LayoutWidgets[L_NETINTERNET].Add("Join");
807     LayoutWidgets[L_NETINTERNET].Add("ServerList");
808     LayoutWidgets[L_NETINTERNET].Add("Refresh");
809     LayoutWidgets[L_NETINTERNET].Add("UpdateList");
810     LayoutWidgets[L_NETINTERNET].Add("AddServer");
811     LayoutWidgets[L_NETINTERNET].Add("Back");
812     LayoutWidgets[L_NETINTERNET].Add("PopupMenu");
813     LayoutWidgets[L_NETINTERNET].Add("PlayerSelection");
814 
815 // L_INTERNETDETAILS: 1 widgets
816     LayoutWidgets[L_INTERNETDETAILS].Add("id_Ok");
817 
818 // L_ADDSERVER: 3 widgets
819     LayoutWidgets[L_ADDSERVER].Add("na_Cancel");
820     LayoutWidgets[L_ADDSERVER].Add("na_Add");
821     LayoutWidgets[L_ADDSERVER].Add("na_Address");
822 
823 // L_NETLAN: 6 widgets
824     LayoutWidgets[L_NETLAN].Add("Join");
825     LayoutWidgets[L_NETLAN].Add("ServerList");
826     LayoutWidgets[L_NETLAN].Add("Refresh");
827     LayoutWidgets[L_NETLAN].Add("Back");
828     LayoutWidgets[L_NETLAN].Add("PopupMenu");
829     LayoutWidgets[L_NETLAN].Add("PlayerSelection");
830 
831 // L_LANDETAILS: 1 widgets
832     LayoutWidgets[L_LANDETAILS].Add("ld_Ok");
833 
834 // L_NETHOST: 10 widgets
835     LayoutWidgets[L_NETHOST].Add("Back");
836     LayoutWidgets[L_NETHOST].Add("Ok");
837     LayoutWidgets[L_NETHOST].Add("PlayerList");
838     LayoutWidgets[L_NETHOST].Add("Playing");
839     LayoutWidgets[L_NETHOST].Add("Servername");
840     LayoutWidgets[L_NETHOST].Add("MaxPlayers");
841     LayoutWidgets[L_NETHOST].Add("Register");
842     LayoutWidgets[L_NETHOST].Add("Password");
843     LayoutWidgets[L_NETHOST].Add("WelcomeMessage");
844     LayoutWidgets[L_NETHOST].Add("AllowWantsJoin");
845 
846 // L_NETFAVOURITES: 7 widgets
847     LayoutWidgets[L_NETFAVOURITES].Add("Join");
848     LayoutWidgets[L_NETFAVOURITES].Add("ServerList");
849     LayoutWidgets[L_NETFAVOURITES].Add("Refresh");
850     LayoutWidgets[L_NETFAVOURITES].Add("Add");
851     LayoutWidgets[L_NETFAVOURITES].Add("Back");
852     LayoutWidgets[L_NETFAVOURITES].Add("PopupMenu");
853     LayoutWidgets[L_NETFAVOURITES].Add("PlayerSelection");
854 
855 // L_FAVOURITESDETAILS: 1 widgets
856     LayoutWidgets[L_FAVOURITESDETAILS].Add("fd_Ok");
857 
858 // L_RENAMESERVER: 3 widgets
859     LayoutWidgets[L_RENAMESERVER].Add("rs_Cancel");
860     LayoutWidgets[L_RENAMESERVER].Add("rs_Ok");
861     LayoutWidgets[L_RENAMESERVER].Add("rs_NewName");
862 
863 // L_ADDFAVOURITE: 4 widgets
864     LayoutWidgets[L_ADDFAVOURITE].Add("fa_Cancel");
865     LayoutWidgets[L_ADDFAVOURITE].Add("fa_Add");
866     LayoutWidgets[L_ADDFAVOURITE].Add("fa_Address");
867     LayoutWidgets[L_ADDFAVOURITE].Add("fa_Name");
868 
869 // L_CONNECTING: 1 widgets
870     LayoutWidgets[L_CONNECTING].Add("Cancel");
871 
872 // L_NETJOINLOBBY: 4 widgets
873     LayoutWidgets[L_NETJOINLOBBY].Add("Back2");
874     LayoutWidgets[L_NETJOINLOBBY].Add("Ready");
875     LayoutWidgets[L_NETJOINLOBBY].Add("ChatText");
876     LayoutWidgets[L_NETJOINLOBBY].Add("ChatList");
877 
878 // L_NETHOSTLOBBY: 14 widgets
879     LayoutWidgets[L_NETHOSTLOBBY].Add("Back2");
880     LayoutWidgets[L_NETHOSTLOBBY].Add("Start");
881     LayoutWidgets[L_NETHOSTLOBBY].Add("ChatText");
882     LayoutWidgets[L_NETHOSTLOBBY].Add("ChatList");
883     LayoutWidgets[L_NETHOSTLOBBY].Add("LevelList");
884     LayoutWidgets[L_NETHOSTLOBBY].Add("Lives");
885     LayoutWidgets[L_NETHOSTLOBBY].Add("MaxKills");
886     LayoutWidgets[L_NETHOSTLOBBY].Add("ModName");
887     LayoutWidgets[L_NETHOSTLOBBY].Add("Gametype");
888     LayoutWidgets[L_NETHOSTLOBBY].Add("GameSettings");
889     LayoutWidgets[L_NETHOSTLOBBY].Add("WeaponOptions");
890     LayoutWidgets[L_NETHOSTLOBBY].Add("PopupMenu");
891     LayoutWidgets[L_NETHOSTLOBBY].Add("Banned");
892     LayoutWidgets[L_NETHOSTLOBBY].Add("ServerSettings");
893 
894 // L_SERVERSETTINGS: 7 widgets
895     LayoutWidgets[L_SERVERSETTINGS].Add("ss_Ok");
896     LayoutWidgets[L_SERVERSETTINGS].Add("ss_Cancel");
897     LayoutWidgets[L_SERVERSETTINGS].Add("ss_AllowOnlyList");
898     LayoutWidgets[L_SERVERSETTINGS].Add("ss_WelcomeMessage");
899     LayoutWidgets[L_SERVERSETTINGS].Add("ss_ServerName");
900     LayoutWidgets[L_SERVERSETTINGS].Add("ss_AllowWantsJoin");
901     LayoutWidgets[L_SERVERSETTINGS].Add("ss_MaxPlayers");
902 
903 // L_BANLIST: 4 widgets
904     LayoutWidgets[L_BANLIST].Add("bl_Close");
905     LayoutWidgets[L_BANLIST].Add("bl_Clear");
906     LayoutWidgets[L_BANLIST].Add("bl_Unban");
907     LayoutWidgets[L_BANLIST].Add("bl_ListBox");
908 
909 // L_PLAYERPROFILES: 2 widgets
910     LayoutWidgets[L_PLAYERPROFILES].Add("NewPlayerTab");
911     LayoutWidgets[L_PLAYERPROFILES].Add("ViewPlayersTab");
912 
913 // L_CREATEPLAYER: 12 widgets
914     LayoutWidgets[L_CREATEPLAYER].Add("np_Back");
915     LayoutWidgets[L_CREATEPLAYER].Add("np_Create");
916     LayoutWidgets[L_CREATEPLAYER].Add("np_Name");
917     LayoutWidgets[L_CREATEPLAYER].Add("np_Red");
918     LayoutWidgets[L_CREATEPLAYER].Add("np_Blue");
919     LayoutWidgets[L_CREATEPLAYER].Add("np_Green");
920     LayoutWidgets[L_CREATEPLAYER].Add("np_Type");
921     LayoutWidgets[L_CREATEPLAYER].Add("np_AIDiffLbl");
922     LayoutWidgets[L_CREATEPLAYER].Add("np_AIDiff");
923     LayoutWidgets[L_CREATEPLAYER].Add("np_PlySkin");
924     LayoutWidgets[L_CREATEPLAYER].Add("np_Username");
925     LayoutWidgets[L_CREATEPLAYER].Add("np_Password");
926 
927 // L_VIEWPLAYERS: 12 widgets
928     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Back");
929     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Name");
930     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Red");
931     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Blue");
932     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Green");
933     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Players");
934     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Delete");
935     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Apply");
936     LayoutWidgets[L_VIEWPLAYERS].Add("vp_Type");
937     LayoutWidgets[L_VIEWPLAYERS].Add("vp_AIDiffLbl");
938     LayoutWidgets[L_VIEWPLAYERS].Add("vp_AIDiff");
939     LayoutWidgets[L_VIEWPLAYERS].Add("vp_PlySkin");
940 
941 // L_LEVELEDITOR: 5 widgets
942     LayoutWidgets[L_LEVELEDITOR].Add("map_new");
943     LayoutWidgets[L_LEVELEDITOR].Add("map_random");
944     LayoutWidgets[L_LEVELEDITOR].Add("map_load");
945     LayoutWidgets[L_LEVELEDITOR].Add("map_save");
946     LayoutWidgets[L_LEVELEDITOR].Add("map_quit");
947 
948 // L_NEWDIALOG: 5 widgets
949     LayoutWidgets[L_NEWDIALOG].Add("mn_Cancel");
950     LayoutWidgets[L_NEWDIALOG].Add("mn_Ok");
951     LayoutWidgets[L_NEWDIALOG].Add("mn_Width");
952     LayoutWidgets[L_NEWDIALOG].Add("mn_Height");
953     LayoutWidgets[L_NEWDIALOG].Add("mn_Scheme");
954 
955 // L_SAVELOADLEVEL: 4 widgets
956     LayoutWidgets[L_SAVELOADLEVEL].Add("sl_Cancel");
957     LayoutWidgets[L_SAVELOADLEVEL].Add("sl_Ok");
958     LayoutWidgets[L_SAVELOADLEVEL].Add("sl_FileList");
959     LayoutWidgets[L_SAVELOADLEVEL].Add("sl_FileName");
960 
961 // L_OPTIONS: 3 widgets
962     LayoutWidgets[L_OPTIONS].Add("ControlsTab");
963     LayoutWidgets[L_OPTIONS].Add("GameTab");
964     LayoutWidgets[L_OPTIONS].Add("SystemTab");
965 
966 // L_OPTIONSCONTROLS: 23 widgets
967     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Up");
968     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Down");
969     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Left");
970     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Right");
971     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Shoot");
972     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Jump");
973     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Selweapon");
974     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Rope");
975     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Up");
976     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Down");
977     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Left");
978     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Right");
979     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Shoot");
980     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Jump");
981     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Selweapon");
982     LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Rope");
983     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Chat");
984     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Score");
985     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Health");
986     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_CurSettings");
987     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_TakeScreenshot");
988     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_ViewportManager");
989     LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_SwitchMode");
990 
991 // L_OPTIONSGAME: 9 widgets
992     LayoutWidgets[L_OPTIONSGAME].Add("BloodAmount");
993     LayoutWidgets[L_OPTIONSGAME].Add("Shadows");
994     LayoutWidgets[L_OPTIONSGAME].Add("Particles");
995     LayoutWidgets[L_OPTIONSGAME].Add("OldSkoolRope");
996     LayoutWidgets[L_OPTIONSGAME].Add("AIDifficulty");
997     LayoutWidgets[L_OPTIONSGAME].Add("ShowWormHealth");
998     LayoutWidgets[L_OPTIONSGAME].Add("ColorizeNicks");
999     LayoutWidgets[L_OPTIONSGAME].Add("AutoTyping");
1000     LayoutWidgets[L_OPTIONSGAME].Add("ScreenshotFormat");
1001 
1002 // L_OPTIONSSYSTEM: 12 widgets
1003     LayoutWidgets[L_OPTIONSSYSTEM].Add("Back");
1004     LayoutWidgets[L_OPTIONSSYSTEM].Add("Fullscreen");
1005     LayoutWidgets[L_OPTIONSSYSTEM].Add("OpenGL");
1006     LayoutWidgets[L_OPTIONSSYSTEM].Add("SoundOn");
1007     LayoutWidgets[L_OPTIONSSYSTEM].Add("SoundVolume");
1008     LayoutWidgets[L_OPTIONSSYSTEM].Add("NetworkPort");
1009     LayoutWidgets[L_OPTIONSSYSTEM].Add("NetworkSpeed");
1010     LayoutWidgets[L_OPTIONSSYSTEM].Add("ShowFPS");
1011     LayoutWidgets[L_OPTIONSSYSTEM].Add("ShowPing");
1012     LayoutWidgets[L_OPTIONSSYSTEM].Add("Filtered");
1013     LayoutWidgets[L_OPTIONSSYSTEM].Add("LogConvos");
1014     LayoutWidgets[L_OPTIONSSYSTEM].Add("Apply");
1015 
1016 // L_MESSAGEBOXOK: 1 widgets
1017     LayoutWidgets[L_MESSAGEBOXOK].Add("mb_Ok");
1018 
1019 // L_MESSAGEBOXYESNO: 2 widgets
1020     LayoutWidgets[L_MESSAGEBOXYESNO].Add("mb_Yes");
1021     LayoutWidgets[L_MESSAGEBOXYESNO].Add("mb_No");
1022 }
1023 
1024 
1025 	// Load the level list
1026 	struct LevelComboFiller {
1027 		CCombobox* cmb;
LevelComboFillerDeprecatedGUI::LevelComboFiller1028 		LevelComboFiller(CCombobox* c) : cmb(c) {}
operator ()DeprecatedGUI::LevelComboFiller1029 		bool operator() (const std::string& filename) {
1030 			std::string mapName = CMap::GetLevelName(filename, true);
1031 			if(mapName.size() != 0)
1032 				cmb->addItem(GetBaseFilename(filename), mapName);
1033 
1034 			return true;
1035 		}
1036 	};
1037 
1038 
1039 ///////////////////
1040 // Fill a listbox with the levels
Menu_FillLevelList(CCombobox * cmb,int random)1041 void Menu_FillLevelList(CCombobox *cmb, int random)
1042 {
1043 	cmb->clear();
1044 	cmb->setSorted(SORT_ASC);
1045 	cmb->setUnique(true);
1046 
1047 	LevelComboFiller filler(cmb);
1048 	FindFiles(filler, "levels", false, FM_REG);
1049 
1050 	// Disable sorting and add the random level at the beginning
1051 	cmb->setSorted(SORT_NONE);
1052 	//if(random) // If random is true, we add the 'random' level to the list
1053 	//	cmb->addItem(0, "_random_", "- Random level -");
1054 
1055 	cmb->setCurSIndexItem(tLXOptions->tGameInfo.sMapFile);
1056 }
1057 
1058 
1059 ///////////////////
1060 // Redraw a section from the buffer to the screen
Menu_redrawBufferRect(int x,int y,int w,int h)1061 void Menu_redrawBufferRect(int x, int y, int w, int h)
1062 {
1063     DrawImageAdv(VideoPostProcessor::videoSurface(), tMenu->bmpBuffer, x,y, x,y, w,h);
1064 }
1065 
1066 
1067 
Menu_DisableNetEvents()1068 void Menu_DisableNetEvents()
1069 {
1070 	for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
1071 		if(i != SCK_FOO)
1072 			tMenu->tSocket[i]->setWithEvents(false);
1073 }
1074 
Menu_EnableNetEvents()1075 void Menu_EnableNetEvents()
1076 {
1077 	for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
1078 		if(i != SCK_FOO)
1079 			tMenu->tSocket[i]->setWithEvents(true);
1080 }
1081 
Menu_IsKeyboardNavigationUsed()1082 bool Menu_IsKeyboardNavigationUsed()
1083 {
1084 	return CGuiLayout::isKeyboardNavigationUsed();
1085 }
1086 
Menu_WarpMouse(int x,int y)1087 void Menu_WarpMouse(int x, int y)
1088 {
1089 	struct RepositionMouse: public Action
1090 	{
1091 		int x, y;
1092 		RepositionMouse(int _x, int _y): x(_x), y(_y)
1093 		{
1094 		}
1095 		int handle()
1096 		{
1097 			SDL_WarpMouse(x, y);
1098 			return true;
1099 		}
1100 	};
1101 	Menu_MouseWarpedX = x;
1102 	Menu_MouseWarpedY = y;
1103 	doActionInMainThread( new RepositionMouse(x, y) );
1104 }
1105 
Menu_ProcessMouseMotion(int x,int y)1106 void Menu_ProcessMouseMotion(int x, int y)
1107 {
1108 	if (x != Menu_MouseWarpedX || y != Menu_MouseWarpedY) {
1109 		CGuiLayout::setKeyboardNavigationUsed(false);
1110 	}
1111 }
1112 
1113 /*
1114 ============================
1115 
1116 	Server list functions
1117 
1118 ============================
1119 */
1120 
1121 
1122 std::list<server_t> psServerList;
1123 
1124 // Maximum number of pings/queries before we ignore the server
1125 static const int	MaxPings = 4;
1126 static const int	MaxQueries = MAX_QUERIES;
1127 
1128 
1129 
1130 ///////////////////
1131 // Clear the server list
Menu_SvrList_Clear()1132 void Menu_SvrList_Clear()
1133 {
1134 	Menu_SvrList_Shutdown();
1135 }
1136 
1137 
1138 ///////////////////
1139 // Clear any servers automatically added
Menu_SvrList_ClearAuto()1140 void Menu_SvrList_ClearAuto()
1141 {
1142     for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1143     {
1144         if(!it->bManual)
1145         {
1146         	psServerList.erase(it);
1147 			if (psServerList.empty())
1148 				return;
1149         	it = psServerList.begin();
1150         }
1151     }
1152 }
1153 
1154 
1155 ///////////////////
1156 // Shutdown the server list
Menu_SvrList_Shutdown()1157 void Menu_SvrList_Shutdown()
1158 {
1159 	psServerList.clear();
1160 }
1161 
1162 
1163 
SendBroadcastPing(int port)1164 static void SendBroadcastPing(int port) {
1165 	// Broadcast a ping on the LAN
1166 	CBytestream bs;
1167 	bs.writeInt(-1,4);
1168 	bs.writeString("lx::ping");
1169 
1170 	NetworkAddr a;
1171 	StringToNetAddr("255.255.255.255", a);
1172 	SetNetAddrPort(a,  port);
1173 	tMenu->tSocket[SCK_LAN]->setRemoteAddress(a);
1174 
1175 	// Send the ping
1176 	bs.Send(tMenu->tSocket[SCK_LAN]);
1177 }
1178 
1179 ///////////////////
1180 // Send a ping out to the LAN (LAN menu)
Menu_SvrList_PingLAN()1181 void Menu_SvrList_PingLAN()
1182 {
1183 	SendBroadcastPing(LX_PORT);
1184 	if(tLXOptions->iNetworkPort != LX_PORT)
1185 		SendBroadcastPing(tLXOptions->iNetworkPort); // try also our own port
1186 }
1187 
1188 
1189 ///////////////////
1190 // Ping a server
Menu_SvrList_PingServer(server_t * svr)1191 void Menu_SvrList_PingServer(server_t *svr)
1192 {
1193 	// If not available, probably the network is not connected right now.
1194 	if(!IsNetAddrAvailable(svr->getBestAddress())) return;
1195 
1196 	for( int af = 0; af < 2; af++ )
1197 	{
1198 		NetworkAddr addr = af ? svr->sAddress6 : svr->sAddress;
1199 		if( !IsNetAddrValid(addr) )
1200 			continue;
1201 		if( af == 0 )
1202 		{
1203 			if( svr->ports.size() == 0 )
1204 			{
1205 				errors << "svr->ports.size() == 0 at " << FILELINE << endl;
1206 				continue;
1207 			}
1208 
1209 			svr->lastPingedPort++;
1210 			if( svr->lastPingedPort >= (int)svr->ports.size() || svr->lastPingedPort < 0 )
1211 				svr->lastPingedPort = 0;
1212 			SetNetAddrPort(addr, svr->ports[svr->lastPingedPort].first);
1213 		}
1214 
1215 		//hints << "Pinging server " << tmp << " real addr " << svr->szAddress << " name " << svr->szName << endl;
1216 		tMenu->tSocket[SCK_NET]->setRemoteAddress(addr);
1217 
1218 		CBytestream bs;
1219 		bs.writeInt(-1,4);
1220 		bs.writeString("lx::ping");
1221 		bs.Send(tMenu->tSocket[SCK_NET]);
1222 	}
1223 
1224 	svr->bProcessing = true;
1225 	svr->nPings++;
1226 	svr->fLastPing = tLX->currentTime;
1227 }
1228 
1229 ///////////////////
1230 // Send Wants To Join message
Menu_SvrList_WantsJoin(const std::string & Nick,server_t * svr)1231 void Menu_SvrList_WantsJoin(const std::string& Nick, server_t *svr)
1232 {
1233 	tMenu->tSocket[SCK_NET]->setRemoteAddress(svr->getBestAddress());
1234 
1235 	CBytestream bs;
1236 	bs.writeInt(-1,4);
1237 
1238 	if( svr->bBehindNat )
1239 	{
1240 		NetworkAddr masterserverAddr;
1241 		SetNetAddrValid(masterserverAddr, false);
1242 		NetworkAddr ignored;
1243 
1244 		if( ! GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored ) )
1245 		{
1246 			GetNetAddrFromNameAsync( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress) );
1247 		}
1248 
1249 		for( int count = 0; !IsNetAddrValid(masterserverAddr) && count < 5; count++ )
1250 		{
1251 			SDL_Delay(20);
1252 			GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored );
1253 		}
1254 
1255 		if( !IsNetAddrValid(masterserverAddr) )
1256 			return;
1257 
1258 		tMenu->tSocket[SCK_NET]->setRemoteAddress(masterserverAddr);
1259 		bs.writeString("lx::traverse");
1260 		bs.writeString(svr->szAddress);
1261 	}
1262 
1263 	bs.writeString("lx::wantsjoin");
1264 	bs.writeString(RemoveSpecialChars(Nick));
1265 	bs.Send(tMenu->tSocket[SCK_NET]);
1266 }
1267 
1268 ///////////////////
1269 // Get server info
Menu_SvrList_GetServerInfo(server_t * svr)1270 void Menu_SvrList_GetServerInfo(server_t *svr)
1271 {
1272 	// Send a getinfo request
1273 	tMenu->tSocket[SCK_NET]->setRemoteAddress(svr->getBestAddress());
1274 
1275 	CBytestream bs;
1276 	bs.writeInt(-1,4);
1277 
1278 	if( svr->bBehindNat )
1279 	{
1280 		NetworkAddr masterserverAddr;
1281 		SetNetAddrValid(masterserverAddr, false);
1282 		NetworkAddr ignored;
1283 
1284 		if( ! GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored ) )
1285 		{
1286 			GetNetAddrFromNameAsync( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress) );
1287 		}
1288 
1289 		for( int count = 0; !IsNetAddrValid(masterserverAddr) && count < 5; count++ )
1290 		{
1291 			SDL_Delay(20);
1292 			GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored );
1293 		}
1294 
1295 		if( !IsNetAddrValid(masterserverAddr) )
1296 			return;
1297 
1298 		tMenu->tSocket[SCK_NET]->setRemoteAddress(masterserverAddr);
1299 		bs.writeString("lx::traverse");
1300 		bs.writeString(svr->szAddress);
1301 	}
1302 
1303 	bs.writeString("lx::getinfo");
1304 	bs.Send(tMenu->tSocket[SCK_NET]);
1305 }
1306 
1307 ///////////////////
1308 // Query a server
Menu_SvrList_QueryServer(server_t * svr)1309 void Menu_SvrList_QueryServer(server_t *svr)
1310 {
1311 	for( int af = 0; af < 2; af++ )
1312 	{
1313 		NetworkAddr addr = af ? svr->sAddress6 : svr->sAddress;
1314 		if( !IsNetAddrValid(addr) )
1315 			continue;
1316 
1317 		tMenu->tSocket[SCK_NET]->setRemoteAddress(addr);
1318 
1319 		CBytestream bs;
1320 		bs.writeInt(-1,4);
1321 		bs.writeString("lx::query");
1322 		bs.writeByte(svr->nQueries);
1323 		bs.Send(tMenu->tSocket[SCK_NET]);
1324 	}
1325 	svr->fQueryTimes[svr->nQueries] = tLX->currentTime;
1326 
1327 	svr->bProcessing = true;
1328 	svr->nQueries++;
1329 	svr->fLastQuery = tLX->currentTime;
1330 }
1331 
1332 
1333 ///////////////////
1334 // Refresh the server list (Internet menu)
Menu_SvrList_RefreshList()1335 void Menu_SvrList_RefreshList()
1336 {
1337 	// Set all the servers to be pinged
1338 	for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1339 	{
1340 		if( ! it->bBehindNat )
1341 			Menu_SvrList_RefreshServer(&(*it), false);
1342 	}
1343 
1344 	// Update the GUI
1345 	Timer("Menu_SvrList_RefreshList ping waiter", null, NULL, PingWait, true).startHeadless();
1346 
1347 	//Menu_SvrList_UpdateUDPList(); // It adds duplicate server entries
1348 }
1349 
1350 
1351 ///////////////////
1352 // Refresh a single server
Menu_SvrList_RefreshServer(server_t * s,bool updategui)1353 void Menu_SvrList_RefreshServer(server_t *s, bool updategui)
1354 {
1355 	if (!tLX)
1356 		return;
1357 
1358     s->bProcessing = true;
1359 	s->bgotPong = false;
1360 	s->bgotQuery = false;
1361 	s->bgotQuery6 = false;
1362 	s->bIgnore = false;
1363 	s->fLastPing = AbsTime();
1364 	s->fLastQuery = AbsTime();
1365 	s->nPings = 0;
1366 	s->fInitTime = tLX->currentTime;
1367 	s->nQueries = 0;
1368 	s->nPing = 0;
1369 	s->nPing4 = s->nPing6 = 9999;
1370 	s->bAddrReady = IsNetAddrValid(s->sAddress) || IsNetAddrValid(s->sAddress6);
1371 	s->lastPingedPort = 0;
1372 
1373 	if( s->ports.size() == 0 )
1374 	{
1375 		int port = IsNetAddrValid(s->sAddress) ? GetNetAddrPort(s->sAddress) : 0;
1376 		if (port == 0)
1377 			port = IsNetAddrValid(s->sAddress6) ?GetNetAddrPort(s->sAddress6) : 0;
1378 		if (port == 0)
1379 			port = LX_PORT;
1380 		s->ports.push_back(std::make_pair(port, -1));
1381 	}
1382 
1383 	if (updategui)
1384 		Timer("Menu_SvrList_RefreshServer ping waiter", null, NULL, PingWait, true).startHeadless();
1385 }
1386 
1387 
1388 ///////////////////
1389 // Add a server onto the list (for list and manually)
Menu_SvrList_AddServer(const std::string & address,bool bManual,const std::string & name,int udpMasterserverIndex,const std::string & v4address)1390 server_t *Menu_SvrList_AddServer(const std::string& address, bool bManual, const std::string & name, int udpMasterserverIndex, const std::string& v4address)
1391 {
1392     // Check if the server is already in the list
1393     // If it is, don't bother adding it
1394 	NetworkAddr ad;
1395 	std::string tmp_address = address;
1396 	TrimSpaces(tmp_address);
1397 	int port = -1;
1398 	if(StringToNetAddr(tmp_address, ad))
1399 	{
1400 		port = GetNetAddrPort(ad);
1401 		if( port == 0 )
1402 			port = LX_PORT;
1403 	}
1404 
1405 	server_t * found = Menu_SvrList_FindServerStr(tmp_address, name);
1406 	if( !found && v4address != "" )
1407 		found = Menu_SvrList_FindServerStr(v4address, name);
1408 
1409 	if( found && port != -1 && port != 0 ) {
1410 		if( found->szName == "Untitled" ) {
1411 			found->szName = name;
1412 			TrimSpaces(found->szName);
1413 		}
1414 		//hints << "Menu_SvrList_AddServer(): merging duplicate " << found->szName << " " << found->szAddress << endl;
1415 		if( StringToNetAddr(tmp_address, ad) ) {
1416 			SetNetAddrPort(ad, port);
1417 			if( IsNetAddrV6(tmp_address) ) {
1418 				if( !AreNetAddrEqual(ad, found->sAddress6) ) {
1419 					// Query it again, to measure v4 ping vs v6 ping
1420 					found->fLastQuery = AbsTime();
1421 				}
1422 				found->sAddress6 = ad;
1423 			} else {
1424 				if( !IsNetAddrValid(found->sAddress) && IsNetAddrValid(found->sAddress6) ) {
1425 					// Query it again, to measure v4 ping vs v6 ping
1426 					found->fLastQuery = AbsTime();
1427 				}
1428 				found->sAddress = ad;
1429 				for( size_t i = 0; i < found->ports.size(); i++ )
1430 					if( found->ports[i].first == port )
1431 						return found;
1432 				found->ports.push_back( std::make_pair( port, udpMasterserverIndex ) );
1433 			}
1434 		}
1435 
1436 		return found;
1437 	}
1438 
1439     // Didn't find one, so create it
1440     psServerList.push_back(server_t());
1441 	server_t * svr = & psServerList.back();
1442 
1443 	// Fill in the details
1444     svr->bManual = bManual;
1445 	svr->szAddress = tmp_address;
1446 	ResetNetAddr(svr->sAddress);
1447 	ResetNetAddr(svr->sAddress6);
1448 
1449 	if( StringToNetAddr(tmp_address, ad) ) {
1450 		SetNetAddrPort(ad, port);
1451 		if( IsNetAddrV6(tmp_address) ) {
1452 			svr->sAddress6 = ad;
1453 			if (v4address != "" && StringToNetAddr(v4address, ad)) {
1454 				port = GetNetAddrPort(ad);
1455 				if( port == 0 )
1456 					port = LX_PORT;
1457 				SetNetAddrPort(ad, port);
1458 				svr->sAddress = ad;
1459 			}
1460 		} else {
1461 			svr->sAddress = ad;
1462 		}
1463 	} else {
1464 		hints << "Menu_SvrList_AddServer(): cannot parse server addr " << svr->szAddress << endl;
1465 	}
1466 
1467 	Menu_SvrList_RefreshServer(svr, bManual);
1468 
1469 	if( svr->ports.size() > 0 )
1470 		svr->ports[0].second = udpMasterserverIndex;
1471 
1472 	// Default game details
1473 	svr->szName = name;
1474 	TrimSpaces(svr->szName);
1475 	svr->nMaxPlayers = 0;
1476 	svr->nNumPlayers = 0;
1477 	svr->nState = 0;
1478 	svr->nPing = -3; // Put it at the end of server list, after NAT servers
1479 	svr->nPing4 = svr->nPing6 = 9999;
1480 	if( udpMasterserverIndex >= 0 )
1481 	{
1482 		svr->bBehindNat = true;
1483 		svr->nPing = -2;
1484 	}
1485 	else
1486 		svr->bBehindNat = false;
1487 
1488 	//hints << "Menu_SvrList_AddServer(): added server " << svr->szName << " svr->szAddress " << svr->szAddress << " v4addr " << v4address <<
1489 	//		" svr->sAddress " << NetAddrToString(svr->sAddress) << " svr->sAddress6 " << NetAddrToString(svr->sAddress6) << endl;
1490 
1491 	return svr;
1492 }
1493 
1494 
1495 ///////////////////
1496 // Remove a server from the server list
Menu_SvrList_RemoveServer(const std::string & szAddress)1497 void Menu_SvrList_RemoveServer(const std::string& szAddress)
1498 {
1499 	for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1500 		if( it->szAddress == szAddress )
1501 		{
1502 			psServerList.erase( it );
1503 			it = psServerList.begin();
1504 			break;
1505 		}
1506 }
1507 
1508 
1509 ///////////////////
1510 // Find a server based on a string address
Menu_SvrList_FindServerStr(const std::string & szAddress,const std::string & name)1511 server_t *Menu_SvrList_FindServerStr(const std::string& szAddress, const std::string & name)
1512 {
1513 	NetworkAddr addr;
1514 	if( ! StringToNetAddr(szAddress, addr) )
1515 		return NULL;
1516 
1517     return Menu_SvrList_FindServer(addr, name);
1518 }
1519 
1520 
1521 ///////////////////
1522 // Fill a listview box with the server list
Menu_SvrList_FillList(CListview * lv)1523 void Menu_SvrList_FillList(CListview *lv)
1524 {
1525 	if (!lv)
1526 		return;
1527 
1528 	std::string		addr;
1529 	static const std::string states[] = {"Open", "Loading", "Playing", "Open/Loading", "Open/Playing"};
1530 
1531     // Store the ID of the currently selected item
1532     int curID = lv->getSelectedID();
1533 
1534 	lv->SaveScrollbarPos();
1535 	lv->Clear();
1536 
1537 	for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1538 	{
1539 
1540 		bool processing = s->bProcessing && Menu_SvrList_GetUdpMasterserverForServer( s->szAddress ) == "";
1541 
1542 		// Ping Image
1543 		int num = 3;
1544 		if(s->nPing < 700)  num = 2;
1545 		if(s->nPing < 400)  num = 1;
1546 		if(s->nPing < 200)  num = 0;
1547 
1548 		if(s->bIgnore || processing)
1549 			num = 3;
1550 
1551 		if(s->nPing == -2)	num = 4; // Server behind a NAT
1552 
1553 		// Address
1554 		//GetRemoteNetAddr(tMenu->tSocket, &s->sAddress);
1555 		//NetAddrToString(&s->sAddress, addr);
1556 
1557 		// show port if special
1558 		addr = s->szAddress;
1559 		size_t p = addr.rfind(':');
1560 		if( IsNetAddrV6(addr) && addr.find("]:") == std::string::npos ) {
1561 			p = std::string::npos;
1562 		}
1563 
1564 		if(p != std::string::npos) {
1565 			std::string sPort = addr.substr(p + 1);
1566 			addr.erase(p);
1567 			if(from_string<int>(sPort) != LX_PORT)
1568 				addr += ":" + sPort;
1569 		}
1570 
1571 		// State
1572 		int state = 0;
1573 		if(s->nState >= 0 && s->nState < 3)
1574 			state = s->nState;
1575 		if( state != 0 && s->bAllowConnectDuringGame && s->nNumPlayers < s->nMaxPlayers )
1576 			state += 2;
1577 
1578 		// Colour
1579 		Color colour = tLX->clListView;
1580 		if(processing)
1581 			colour = tLX->clDisabled;
1582 
1583 		// Add the server to the list
1584 		lv->AddItem(s->szAddress, 0, colour);
1585 		lv->AddSubitem(LVS_IMAGE, itoa(num,10), tMenu->bmpConnectionSpeeds[num], NULL);
1586 		lv->AddSubitem(LVS_TEXT, s->szName, (DynDrawIntf*)NULL, NULL);
1587         if(processing) {
1588 			if(IsNetAddrValid(s->sAddress) || IsNetAddrValid(s->sAddress6))
1589 				lv->AddSubitem(LVS_TEXT, "Querying...", (DynDrawIntf*)NULL, NULL);
1590 			else
1591 				lv->AddSubitem(LVS_TEXT, "Lookup...", (DynDrawIntf*)NULL, NULL);
1592         } else if( num == 3 )
1593             lv->AddSubitem(LVS_TEXT, "Down", (DynDrawIntf*)NULL, NULL);
1594         else
1595 		    lv->AddSubitem(LVS_TEXT, states[state], (DynDrawIntf*)NULL, NULL);
1596 
1597 		bool unknownData = ( s->bProcessing || num == 3 ) &&
1598 							Menu_SvrList_GetUdpMasterserverForServer( s->szAddress ) == "";
1599 
1600 		// Players
1601 		lv->AddSubitem(LVS_TEXT,
1602 					   unknownData ? "?" : (itoa(s->nNumPlayers,10)+"/"+itoa(s->nMaxPlayers,10)),
1603 					   (DynDrawIntf*)NULL, NULL);
1604 
1605 		if (s->nPing <= -2) // Server behind a NAT or not queried, it will add spaces if s->nPing == -3 so not queried servers will be below NAT ones
1606 			lv->AddSubitem(LVS_TEXT, "N/A" + std::string(' ', -2 - s->nPing), (DynDrawIntf*)NULL, NULL);
1607 		else
1608 			lv->AddSubitem(LVS_TEXT, unknownData ? "∞" : itoa(s->nPing,10), (DynDrawIntf*)NULL, NULL); // TODO: the infinity symbol isn't shown correctly
1609 
1610 		// Country
1611 		if (tLXOptions->bUseIpToCountry && iNetMode == net_internet) {
1612 			IpInfo inf = tIpToCountryDB->GetInfoAboutIP(addr);
1613 			if( tLXOptions->bShowCountryFlags )
1614 			{
1615 				SmartPointer<SDL_Surface> flag = tIpToCountryDB->GetCountryFlag(inf.countryCode);
1616 				if (flag.get())
1617 					lv->AddSubitem(LVS_IMAGE, "", flag, NULL, VALIGN_MIDDLE, inf.countryName);
1618 				else
1619 					lv->AddSubitem(LVS_TEXT, inf.countryCode, (DynDrawIntf*)NULL, NULL);
1620 			}
1621 			else
1622 			{
1623 				lv->AddSubitem(LVS_TEXT, inf.countryName, (DynDrawIntf*)NULL, NULL);
1624 			}
1625 		}
1626 
1627 		// Address
1628 		lv->AddSubitem(LVS_TEXT, addr, (DynDrawIntf*)NULL, NULL);
1629 	}
1630 
1631 	lv->ReSort();
1632     lv->setSelectedID(curID);
1633 	lv->RestoreScrollbarPos();
1634 }
1635 
1636 static bool bUpdateFromUdpThread = false;
1637 ///////////////////
1638 // Process the network connection
1639 // Returns true if a server in the list was added/modified
Menu_SvrList_Process()1640 bool Menu_SvrList_Process()
1641 {
1642 	CBytestream		bs;
1643 	bool			update = false;
1644 
1645 
1646 	// Process any packets on the net socket
1647 	while(bs.Read(tMenu->tSocket[SCK_NET])) {
1648 
1649 		if( Menu_SvrList_ParsePacket(&bs, tMenu->tSocket[SCK_NET]) )
1650 			update = true;
1651 
1652 	}
1653 
1654 	// Process any packets on the LAN socket
1655 	while(bs.Read(tMenu->tSocket[SCK_LAN])) {
1656 
1657 		if( Menu_SvrList_ParsePacket(&bs, tMenu->tSocket[SCK_LAN]) )
1658 			update = true;
1659 	}
1660 
1661 	if( bUpdateFromUdpThread )
1662 	{
1663 		bUpdateFromUdpThread = false;
1664 		update = true;
1665 	}
1666 
1667 	bool repaint = false;
1668 
1669 
1670 	// Ping or Query any servers in the list that need it
1671 	for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1672 	{
1673 
1674 		// Ignore this server? (timed out)
1675 		if(s->bIgnore)
1676 			continue;
1677 
1678 		if(!IsNetAddrValid(s->sAddress) && !IsNetAddrValid(s->sAddress6)) {
1679 			if(tLX->currentTime - s->fInitTime >= DNS_TIMEOUT) {
1680 				s->bIgnore = true; // timeout
1681 				update = true;
1682 			}
1683 			continue;
1684 		} else {
1685 			if(!s->bAddrReady) {
1686 				s->bAddrReady = true;
1687 				update = true;
1688 			}
1689 		}
1690 
1691 		// Need a pingin'?
1692 		if(!s->bgotPong) {
1693 			if(tLX->currentTime - s->fLastPing > (float)PingWait / 1000.0f) {
1694 
1695 				if(s->nPings >= MaxPings) {
1696 					s->bIgnore = true;
1697 
1698 					update = true;
1699 				} else  {
1700 					// Ping the server
1701 					Menu_SvrList_PingServer(&(*s));
1702 					repaint = true;
1703 				}
1704 			}
1705 		}
1706 
1707 		// Need a querying?
1708 		if(s->bgotPong && (
1709 			(IsNetAddrValid(s->sAddress) && !s->bgotQuery) ||
1710 			(IsNetAddrValid(s->sAddress6) && !s->bgotQuery6))) {
1711 			if(tLX->currentTime - s->fLastQuery > (float)QueryWait / 1000.0f) {
1712 
1713 				if(s->nQueries >= MaxQueries) {
1714 					if( !s->bgotQuery && !s->bgotQuery6 ) {
1715 						s->bIgnore = true;
1716 						update = true;
1717 					}
1718 				} else  {
1719 					// Query the server
1720 					Menu_SvrList_QueryServer(&(*s));
1721 					repaint = true;
1722 				}
1723 			}
1724 		}
1725 
1726 		// If we are ignoring this server now, set it to not processing
1727 		if(s->bIgnore) {
1728 			s->bProcessing = false;
1729 			update = true;
1730 		}
1731 
1732 	}
1733 
1734 	// Make sure the list repaints when the ping/query is received
1735 	if (repaint)
1736 		Timer("Menu_SvrList_Process ping waiter", null, NULL, PingWait + 100, true).startHeadless();
1737 
1738 	return update;
1739 }
1740 
1741 
1742 ///////////////////
1743 // Parse a packet
1744 // Returns true if we should update the list
Menu_SvrList_ParsePacket(CBytestream * bs,const SmartPointer<NetworkSocket> & sock)1745 bool Menu_SvrList_ParsePacket(CBytestream *bs, const SmartPointer<NetworkSocket>& sock)
1746 {
1747 	NetworkAddr		adrFrom;
1748 	bool			update = false;
1749 	std::string cmd,buf;
1750 
1751 	// Check for connectionless packet header
1752 	if(bs->readInt(4) == -1) {
1753 		cmd = bs->readString();
1754 
1755 		adrFrom = sock->remoteAddress();
1756 
1757 
1758 		// Check for a pong
1759 		if(cmd == "lx::pong") {
1760 
1761 			// Look the the list and find which server returned the ping
1762 			server_t *svr = Menu_SvrList_FindServer(adrFrom);
1763 			if( svr ) {
1764 
1765 				// It pinged, so fill in the ping info so it will now be queried
1766 				svr->bgotPong = true;
1767 				svr->nQueries = 0;
1768 				svr->bBehindNat = false;
1769 				svr->lastPingedPort = 0;
1770 				if( AreNetAddrEqual(adrFrom, svr->sAddress6) ) {
1771 					//NetAddrToString(svr->sAddress6, svr->szAddress);
1772 				} else {
1773 					SetNetAddrPort(svr->sAddress, GetNetAddrPort(adrFrom));
1774 					//NetAddrToString(svr->sAddress, svr->szAddress);
1775 				}
1776 				svr->ports.clear();
1777 				svr->ports.push_back( std::make_pair( (int)GetNetAddrPort(adrFrom), -1 ) );
1778 
1779 			} else {
1780 
1781 				// If we didn't ping this server directly (eg, subnet), add the server to the list
1782 				// HINT: in favourites list, only user should add servers
1783 				if (iNetMode != net_favourites)  {
1784 					NetAddrToString( adrFrom, buf );
1785 					svr = Menu_SvrList_AddServer(buf, false);
1786 
1787 					if( svr ) {
1788 
1789 						// Only update the list if this is the first ping
1790 						if(!svr->bgotPong)
1791 							update = true;
1792 
1793 						// Set it the ponged
1794 						svr->bgotPong = true;
1795 						svr->nQueries = 0;
1796 
1797 						//Menu_SvrList_RemoveDuplicateNATServers(svr); // We don't know the name of server yet
1798 					}
1799 				}
1800 			}
1801 		}
1802 
1803 		// Check for a query return
1804 		else if(cmd == "lx::queryreturn") {
1805 
1806 			// Look the the list and find which server returned the ping
1807 			server_t *svr = Menu_SvrList_FindServer(adrFrom);
1808 			if( svr ) {
1809 				bool ipv6 = AreNetAddrEqual(adrFrom, svr->sAddress6);
1810 
1811 				// Only update the list if this is the first query
1812 				if(ipv6) {
1813 					if(!svr->bgotQuery6)
1814 						update = true;
1815 				} else {
1816 					if(!svr->bgotQuery)
1817 						update = true;
1818 				}
1819 
1820 				svr->bBehindNat = false;
1821 				Menu_SvrList_ParseQuery(svr, bs, ipv6);
1822 
1823 			}
1824 
1825 			// If we didn't query this server, then we should ignore it
1826 		}
1827 
1828 		else if(cmd == "lx::serverlist2") // This should not happen, we have another thread for polling UDP servers
1829 		{
1830 			Menu_SvrList_ParseUdpServerlist(bs, 0);
1831 			update = true;
1832 		}
1833 		else if(cmd == "lx::serverlist3") // This should not happen, we have another thread for polling UDP servers
1834 		{
1835 			Menu_SvrList_ParseUdpServerlist(bs, 0, true);
1836 			update = true;
1837 		}
1838 
1839 	}
1840 
1841 	return update;
1842 }
1843 
1844 
1845 ///////////////////
1846 // Find a server from the list by address
Menu_SvrList_FindServer(const NetworkAddr & addr,const std::string & name)1847 server_t *Menu_SvrList_FindServer(const NetworkAddr& addr, const std::string & name)
1848 {
1849 	for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1850 	{
1851 		if( AreNetAddrEqual( addr, s->sAddress ) )
1852 			return &(*s);
1853 		if( AreNetAddrEqual( addr, s->sAddress6 ) )
1854 			return &(*s);
1855 	}
1856 
1857     NetworkAddr addr1 = addr;
1858     SetNetAddrPort(addr1, LX_PORT);
1859 
1860 	for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1861 	{
1862 		// Check if any port number match from the server entry
1863 		NetworkAddr addr2 = s->sAddress;
1864 		for( size_t i = 0; i < s->ports.size(); i++ )
1865 		{
1866 			SetNetAddrPort(addr2, s->ports[i].first);
1867 			if( AreNetAddrEqual( addr, addr2 ) )
1868 				return &(*s);
1869 		}
1870 
1871 		// Check if IP without port and name match
1872 		SetNetAddrPort(addr2, LX_PORT);
1873 		if( AreNetAddrEqual( addr1, addr2 ) && name == s->szName && name != "Untitled" )
1874 			return &(*s);
1875 	}
1876 
1877 	// None found
1878 	return NULL;
1879 }
1880 
1881 
1882 ///////////////////
1883 // Parse the server query return packet
Menu_SvrList_ParseQuery(server_t * svr,CBytestream * bs,bool ipv6)1884 void Menu_SvrList_ParseQuery(server_t *svr, CBytestream *bs, bool ipv6)
1885 {
1886 	// TODO: move this net protocol stuff out here
1887 
1888 	// Don't update the name in favourites
1889 	std::string buf = Utf8String(bs->readString());
1890 	if(iNetMode != net_favourites)
1891 		svr->szName = buf;
1892 	TrimSpaces(svr->szName);
1893 	//hints << "Menu_SvrList_ParseQuery(): " << svr->szName << " " << svr->szAddress << endl;
1894 	svr->nNumPlayers = bs->readByte();
1895 	svr->nMaxPlayers = bs->readByte();
1896 	svr->nState = bs->readByte();
1897     int num = bs->readByte();
1898 	svr->bProcessing = false;
1899 	svr->bAllowConnectDuringGame = false;
1900 	svr->tVersion.reset();
1901 
1902 	if(num < 0 || num >= MAX_QUERIES-1)
1903 		num=0;
1904 
1905 	int ping = (int)( (tLX->currentTime - svr->fQueryTimes[num]).milliseconds() );
1906 
1907 	if (ipv6) {
1908 		if (svr->bgotQuery6 && svr->nPing6 > ping)
1909 			svr->nPing6 = ping;
1910 		svr->bgotQuery6 = true;
1911 	} else {
1912 		if (svr->bgotQuery && svr->nPing > ping)
1913 			svr->nPing4 = ping;
1914 		svr->nPing4 = ping;
1915 		svr->bgotQuery = true;
1916 	}
1917 
1918 	if (svr->nPing4 < svr->nPing6)
1919 	{
1920 		svr->nPing = svr->nPing4;
1921 		NetAddrToString(svr->sAddress, svr->szAddress);
1922 	}
1923 	else
1924 	{
1925 		svr->nPing = svr->nPing6;
1926 		NetAddrToString(svr->sAddress6, svr->szAddress);
1927 	}
1928 
1929 	if(svr->nPing < 0)
1930 		svr->nPing = 999;
1931     if(svr->nPing > 999)
1932         svr->nPing = 999;
1933 
1934 	if( !bs->isPosAtEnd() )
1935 	{
1936 		// Beta8+
1937 		svr->tVersion.setByString( bs->readString(64) );
1938 		svr->bAllowConnectDuringGame = bs->readBool();
1939 	}
1940 	//hints << "Menu_SvrList_ParseQuery(): " << svr->szName << " addr " << svr->szAddress << " v4 " << NetAddrToString(svr->sAddress) <<
1941 	//		" v6 " << NetAddrToString(svr->sAddress6) << " ping " << svr->nPing << " ping4 " << svr->nPing4 << " ping6 " << svr->nPing6 << endl;
1942 	// We got server name in a query. let's remove servers with the same name and IP, which we got from UDP masterserver
1943 	// Duplicates should be detected in AddServer(), but some servers fail detection because of different port
1944 	for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1945 	{
1946 		NetworkAddr addr1 = it->sAddress;
1947 		SetNetAddrPort(addr1, LX_PORT);
1948 		NetworkAddr addr2 = svr->sAddress;
1949 		SetNetAddrPort(addr2, LX_PORT);
1950 		if( it->szName == svr->szName && AreNetAddrEqual(addr1, addr2) && svr != &(*it) )
1951 		{
1952 			//Duplicate server - delete it
1953 			//hints << "Menu_SvrList_ParseQuery(): removing duplicate " << it->szName << " " << it->szAddress << endl;
1954 			if (IsNetAddrValid(it->sAddress6) && !IsNetAddrValid(svr->sAddress6)) {
1955 				svr->sAddress6 = it->sAddress6;
1956 				svr->nPing6 = it->nPing6;
1957 				svr->bgotQuery6 = it->bgotQuery6;
1958 			}
1959 			psServerList.erase(it);
1960 			it = psServerList.begin();
1961 		}
1962 	}
1963 }
1964 
1965 /*************************
1966 *
1967 * UDP server list
1968 *
1969 ************************/
1970 
1971 static std::list<std::string> tUdpMasterServers;
1972 static std::map<size_t, ThreadPoolItem *> tUpdateThreads;
1973 static size_t threadId = 0;
1974 
1975 struct UdpServerlistData  {
1976 	CBytestream *bs;
1977 	int UdpServerIndex;
1978 	bool v4addrIncluded;
UdpServerlistDataDeprecatedGUI::UdpServerlistData1979 	UdpServerlistData(CBytestream *b, int _UdpServerIndex, bool _v4addrIncluded) : bs(b), UdpServerIndex(_UdpServerIndex), v4addrIncluded(_v4addrIncluded) {}
1980 };
1981 
Menu_UpdateUDPListEventHandler(UdpServerlistData data)1982 void Menu_UpdateUDPListEventHandler(UdpServerlistData data)
1983 {
1984 	if (iNetMode == net_internet) // Only add them if the Internet tab is active
1985 		Menu_SvrList_ParseUdpServerlist(data.bs, data.UdpServerIndex, data.v4addrIncluded);
1986 	delete data.bs;
1987 }
1988 
Menu_UpdateUDPListEnd(size_t thread)1989 void Menu_UpdateUDPListEnd(size_t thread)
1990 {
1991 	std::map<size_t, ThreadPoolItem *>::iterator it = tUpdateThreads.find(thread);
1992 	if (it != tUpdateThreads.end())
1993 		threadPool->wait(it->second, NULL);
1994 }
1995 
1996 Event<UdpServerlistData> serverlistEvent;
1997 Event<size_t> updateEndEvent;
Menu_SvrList_UpdaterThread(void * id)1998 int Menu_SvrList_UpdaterThread(void *id)
1999 {
2000 	// Setup event handlers
2001 	updateEndEvent.handler() = getEventHandler(&Menu_UpdateUDPListEnd);
2002 	serverlistEvent.handler() = getEventHandler(&Menu_UpdateUDPListEventHandler);
2003 
2004 	// Open socket for networking
2005 	NetworkSocket sock;
2006 	if (!sock.OpenUnreliable(0))  {
2007 		updateEndEvent.pushToMainQueue((size_t)id);
2008 		return -1;
2009 	}
2010 
2011 	// Get serverlist from all the servers in the file
2012 	int UdpServerIndex = 0;
2013 	for (std::list<std::string>::iterator it = tUdpMasterServers.begin(); it != tUdpMasterServers.end(); ++it, ++UdpServerIndex)
2014 	{
2015 		std::string& server = *it;
2016 		NetworkAddr addr, addr6;
2017 		if (server.rfind(':') == std::string::npos)
2018 			server += ":23450";  // Default port
2019 
2020 		// Split to domain and port
2021 		std::string domain = server.substr(0, server.rfind(':'));
2022 		int port = atoi(server.substr(server.rfind(':') + 1));
2023 
2024 		// Resolve the address
2025 		if( ! GetFromDnsCache( domain, addr, addr6 ) )
2026 		{
2027 			GetNetAddrFromNameAsync(domain);
2028 		}
2029 
2030 		AbsTime start = GetTime();
2031 		while (GetTime() - start <= 5.0f) {
2032 			SDL_Delay(40);
2033 			if( GetFromDnsCache( domain, addr, addr6 ) )
2034 				break;
2035 		}
2036 
2037 		if( !IsNetAddrValid(addr) && !IsNetAddrValid(addr6) )
2038 		{
2039 			notes << "UDP masterserver failed: cannot resolve domain name " << domain << endl;
2040 			continue;
2041 		}
2042 
2043 		// Setup the socket
2044 		SetNetAddrPort(addr, port);
2045 		SetNetAddrPort(addr6, port);
2046 
2047 		for (int af = 0; af < 2; af++, addr = addr6)
2048 		{
2049 			if (!IsNetAddrValid(addr))
2050 				continue;
2051 
2052 			sock.setRemoteAddress(addr);
2053 
2054 			// Send the getserverlist packet
2055 			CBytestream *bs = new CBytestream();
2056 			bs->writeInt(-1, 4);
2057 			bs->writeString(af ? "lx::getserverlist3" : "lx::getserverlist2");
2058 			if(!bs->Send(&sock))
2059 			{
2060 				delete bs;
2061 				warnings << "error while sending data to " << server << (af ? " over IPv6" : " over IPv4") << endl;
2062 				continue;
2063 			}
2064 			bs->Clear();
2065 
2066 			//notes << "Sent getserverlist to " << server << endl;
2067 
2068 			// Wait for the reply
2069 			AbsTime timeoutTime = GetTime() + 5.0f;
2070 			bool firstPacket = true;
2071 			while( true ) {
2072 
2073 				while (GetTime() <= timeoutTime)  {
2074 					SDL_Delay(40); // TODO: do it event based
2075 
2076 					// Got a reply?
2077 					if (bs->Read(&sock))  {
2078 						//notes << "Got a reply from " << server << endl;
2079 						break;
2080 					}
2081 
2082 
2083 				}
2084 
2085 				// Parse the reply
2086 				std::string response;
2087 				if (bs->GetLength() && bs->readInt(4) == -1 && (response = bs->readString()).find("lx::serverlist") == 0) {
2088 					serverlistEvent.pushToMainQueue(UdpServerlistData(bs, UdpServerIndex, response == "lx::serverlist3"));
2089 					timeoutTime = GetTime() + 0.5f;	// Check for another packet
2090 					bs = new CBytestream(); // old bs pointer is in mainqueue now
2091 					firstPacket = false;
2092 				} else  {
2093 					if( firstPacket )
2094 						warnings << "Error getting serverlist from " << server << endl;
2095 					delete bs;
2096 					break;
2097 				}
2098 			}
2099 		}
2100 	}
2101 
2102 	// Cleanup
2103 	sock.Close();
2104 
2105 	updateEndEvent.pushToMainQueue((size_t)id);
2106 	return 0;
2107 }
2108 
Menu_SvrList_UpdateUDPList()2109 void Menu_SvrList_UpdateUDPList()
2110 {
2111 	if (tUdpMasterServers.size() == 0)  {  // Load the list of servers only if not already loaded
2112 		// Open the masterservers file
2113 		FILE *fp1 = OpenGameFile("cfg/udpmasterservers.txt", "rt");
2114 		if(!fp1)  {
2115 			warnings << "could not open udpmasterservers.txt file, NAT traversal will be inaccessible" << endl;
2116 			return;
2117 		}
2118 
2119 		// Get the list of servers
2120 		while( !feof(fp1) ) {
2121 			std::string szLine = ReadUntil(fp1);
2122 			TrimSpaces(szLine);
2123 
2124 			if( szLine.length() == 0 )
2125 				continue;
2126 
2127 			tUdpMasterServers.push_back(szLine);
2128 		}
2129 		fclose(fp1);
2130 	}
2131 
2132 	// Run the update
2133 	ThreadPoolItem *thread = threadPool->start(Menu_SvrList_UpdaterThread, (void *)(++threadId), "serverlist updater");
2134 	tUpdateThreads[threadId] = thread;
2135 }
2136 
Menu_SvrList_ParseUdpServerlist(CBytestream * bs,int UdpMasterserverIndex,bool v4AddressIncluded)2137 void Menu_SvrList_ParseUdpServerlist(CBytestream *bs, int UdpMasterserverIndex, bool v4AddressIncluded)
2138 {
2139 	// Look the the list and find which server returned the ping
2140 	int amount = bs->readByte();
2141 	//notes << "Menu_SvrList_ParseUdpServerlist " << amount << endl;
2142 	for( int f=0; f<amount; f++ )
2143 	{
2144 		std::string addr = bs->readString();
2145 		std::string name = bs->readString();
2146 		TrimSpaces(name);
2147 		TrimSpaces(addr);
2148 		//hints << "Menu_SvrList_ParseUdpServerlist(): " << name << " " << addr << endl;
2149 		int players = bs->readByte();
2150 		int maxplayers = bs->readByte();
2151 		int state = bs->readByte();
2152 		Version version = bs->readString(64);
2153 		bool allowConnectDuringGame = bs->readBool();
2154 		std::string v4address;
2155 		if( v4AddressIncluded )
2156 			v4address = bs->readString();
2157 		// UDP server info is updated once per 40 seconds, so if we have more recent entry - ignore it
2158 		server_t *svr = Menu_SvrList_FindServerStr(addr, name);
2159 		if( svr != NULL )
2160 		{
2161 			//hints << "Menu_SvrList_ParseUdpServerlist(): got duplicate " << name << " " << addr << " pong " << svr->bgotPong << " query " << svr->bgotQuery << endl;
2162 			if( svr->bgotPong && IsNetAddrValid(svr->sAddress) && IsNetAddrValid(svr->sAddress6) )
2163 				continue;
2164 			// It will merge existing server with new info
2165 			Menu_SvrList_AddServer(addr, false, name, UdpMasterserverIndex, v4address);
2166 			continue;
2167 		}
2168 
2169 		// In favourites/LAN only the user should add servers
2170 		if (iNetMode == net_internet)  {
2171 			svr = Menu_SvrList_AddServer( addr, false, name, UdpMasterserverIndex, v4address );
2172 			svr->nNumPlayers = players;
2173 			svr->nMaxPlayers = maxplayers;
2174 			svr->nState = state;
2175 			svr->nPing = -2;
2176 			svr->nPing4 = svr->nPing6 = 9999;
2177 			svr->nQueries = 0;
2178 			svr->bgotPong = false;
2179 			svr->bgotQuery = false;
2180 			svr->bgotQuery6 = false;
2181 			svr->bProcessing = false;
2182 			svr->tVersion = version;
2183 			svr->bAllowConnectDuringGame = allowConnectDuringGame;
2184 			svr->bBehindNat = true;
2185 		}
2186 	};
2187 
2188 	bUpdateFromUdpThread = true;
2189 	// Update the GUI when ping times out
2190 	Timer("Menu_SvrList_ParseUdpServerlist ping waiter", null, NULL, PingWait, true).startHeadless();
2191 };
2192 
2193 ///////////////////
2194 // Save the server list
Menu_SvrList_SaveList(const std::string & szFilename)2195 void Menu_SvrList_SaveList(const std::string& szFilename)
2196 {
2197     FILE *fp = OpenGameFile(szFilename,"wt");
2198     if( !fp )
2199         return;
2200 
2201 	for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
2202 	{
2203 		int UdpMasterServer = -1;
2204 		for( size_t port = 0; s->bBehindNat && port < s->ports.size() && UdpMasterServer == -1; port++ )
2205 			if( s->ports[port].second >= 0 )
2206 				UdpMasterServer = s->ports[port].second;
2207 
2208         fprintf(fp, "%s, %s, %s",s->bManual ? "1" : "0", s->szName.c_str(), s->szAddress.c_str() );
2209         if( UdpMasterServer != -1 && !s->bManual )
2210         	fprintf(fp, ", %i", UdpMasterServer );
2211        	fprintf(fp, "\n" );
2212 	}
2213 
2214     fclose(fp);
2215 }
2216 
2217 ///////////////////
2218 // Add a favourite server
Menu_SvrList_AddFavourite(const std::string & szName,const std::string & szAddress)2219 void Menu_SvrList_AddFavourite(const std::string& szName, const std::string& szAddress)
2220 {
2221     FILE *fp = OpenGameFile("cfg/favourites.dat","a");  // We're appending
2222     if( !fp )  {
2223         fp = OpenGameFile("cfg/favourites.dat","wb");  // Try to create the file
2224 		if (!fp)
2225 			return;
2226 	}
2227 
2228 	// Append the server
2229     fprintf(fp,"%s, %s, %s\n","1", szName.c_str(), szAddress.c_str());
2230 
2231     fclose(fp);
2232 }
2233 
2234 
2235 ///////////////////
2236 // Load the server list
Menu_SvrList_LoadList(const std::string & szFilename)2237 void Menu_SvrList_LoadList(const std::string& szFilename)
2238 {
2239     FILE *fp = OpenGameFile(szFilename,"rt");
2240     if( !fp )
2241         return;
2242 
2243     // Go through every line
2244     while( !feof(fp) ) {
2245 		std::string szLine = ReadUntil(fp);
2246         if( szLine == "" )
2247             continue;
2248 
2249 		// explode and copy it
2250 		std::vector<std::string> parsed = explode(szLine,",");
2251 
2252         if( parsed.size() >= 3 ) {
2253 			TrimSpaces(parsed[0]);
2254 			TrimSpaces(parsed[1]);
2255 			TrimSpaces(parsed[2]); // Address
2256 
2257 			int UdpMasterServer = -1;
2258 			if( parsed.size() >= 4 )
2259 				UdpMasterServer = atoi(parsed[3]);
2260 
2261             Menu_SvrList_AddServer(parsed[2], parsed[0] == "1", parsed[1], UdpMasterServer);
2262         }
2263     }
2264 
2265 	// Update the GUI after the ping timed out
2266 	Timer("Menu_SvrList_LoadList ping waiter", null, NULL, PingWait, true).startHeadless();
2267 
2268     fclose(fp);
2269 }
2270 
Menu_SvrList_GetUdpMasterserverForServer(const std::string & addr)2271 std::string Menu_SvrList_GetUdpMasterserverForServer(const std::string & addr)
2272 {
2273 	server_t * svr = Menu_SvrList_FindServerStr(addr);
2274 	if( !svr )
2275 		return "";
2276 	if( !svr->bBehindNat )
2277 		return "";
2278 
2279 	for( size_t port = 0; port < svr->ports.size(); port++ )
2280 	{
2281 		if( svr->ports[port].second < 0 )
2282 			continue;
2283 		int idx = 0;
2284 		for( std::list<std::string>::iterator it = tUdpMasterServers.begin(); it != tUdpMasterServers.end(); ++it, ++idx )
2285 			if( idx == svr->ports[port].second )
2286 				return *it;
2287 	}
2288 
2289 	return "";
2290 }
2291 
2292 
2293 bool bGotDetails = false;
2294 bool bOldLxBug = false;
2295 int nTries = 0;
2296 AbsTime fStart;
2297 CListview lvInfo;
2298 
2299 
2300 ///////////////////
2301 // Draw a 'server info' box
Menu_SvrList_DrawInfo(const std::string & szAddress,int w,int h)2302 void Menu_SvrList_DrawInfo(const std::string& szAddress, int w, int h)
2303 {
2304 	int y = tMenu->bmpBuffer.get()->h/2 - h/2;
2305 	int x = tMenu->bmpBuffer.get()->w/2 - w/2;
2306 
2307 	Menu_redrawBufferRect(x,y,w,h);
2308 
2309     Menu_DrawBox(VideoPostProcessor::videoSurface(), x,y, x+w, y+h);
2310 	DrawRectFillA(VideoPostProcessor::videoSurface(), x+2,y+2, x+w-1, y+h-1, tLX->clDialogBackground, 230);
2311     tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+5, tLX->clNormalLabel, "Server Details");
2312 
2313 
2314 	server_t* svr = Menu_SvrList_FindServerStr(szAddress);
2315 	NetworkAddr origAddr;
2316 	if(svr) {
2317 		if(IsNetAddrValid(svr->sAddress)) {
2318 			origAddr = svr->sAddress;
2319 		} else {
2320 			tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+h/2-8, tLX->clNormalLabel,  "Resolving domain ...");
2321 			return;
2322 		}
2323 	} else {
2324 		warnings << "Querying server not from svr list: " << szAddress << endl;
2325 		std::string tmp_addr = szAddress;
2326 		TrimSpaces(tmp_addr);
2327 		if(!StringToNetAddr(tmp_addr, origAddr)) {
2328 			// TODO: this happens also, if the server is not in the serverlist
2329 			// we should do the domain resolving also here by ourselfs
2330 			tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2,y+tLX->cFont.GetHeight()+10, tLX->clError, "DNS not resolved");
2331 			return;
2332 		}
2333 	}
2334 
2335 
2336     // Get the server details
2337     std::string		szName;
2338     int				nMaxWorms = 0;
2339     int				nState = 0;
2340     std::string		szMapName;
2341     std::string		szModName;
2342     int				nGameMode = 0;
2343     int				nLives = 0;
2344     int				nMaxKills = 0;
2345     int				nLoadingTime = 0;
2346     int				nBonuses = 0;
2347     int				nNumPlayers = 0;
2348 	IpInfo			tIpInfo;
2349 	std::string		sIP;
2350     CWorm			cWorms[MAX_WORMS];
2351 	bool			bHaveLives = false;
2352 	std::string		sServerVersion;
2353 	bool			bHaveGameSpeed = false;
2354 	float			fGameSpeed = 1.0f;
2355 	FeatureCompatibleSettingList features;
2356 
2357     CBytestream inbs;
2358     NetworkAddr   addr;
2359 
2360     if(nTries < 3 && !bGotDetails && !bOldLxBug) {
2361 
2362 		tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+h/2-8, tLX->clNormalLabel,  "Loading info...");
2363 
2364         if (inbs.Read(tMenu->tSocket[SCK_NET])) {
2365             // Check for connectionless packet header
2366 	        if(inbs.readInt(4) == -1) {
2367                 std::string cmd = inbs.readString();
2368 
2369 		        addr = tMenu->tSocket[SCK_NET]->remoteAddress();
2370 
2371 				if(cmd == "lx::traverse") // Response from UDP masterserver
2372 				{
2373 					sIP = inbs.readString();
2374 					StringToNetAddr(sIP, addr);
2375 					if( !inbs.isPosAtEnd() )
2376 						cmd = inbs.readString();
2377 				}
2378 
2379 		        // Check for server info
2380 		        if(cmd == "lx::serverinfo") {
2381                     bGotDetails = true;
2382 
2383 					sServerVersion = "LieroX 0.56";
2384 
2385 					// Get the IP info
2386 					if (NetAddrToString(addr, sIP))
2387 						tIpInfo = tIpToCountryDB->GetInfoAboutIP(sIP);
2388 					else  {
2389 						tIpInfo.countryName = "Hackerland";
2390 						tIpInfo.continent = "Hackerland";
2391 						sIP = "x.x.x.x";
2392 					}
2393 
2394 
2395                     // Read the info
2396                     szName = Utf8String(inbs.readString(64));
2397 	                nMaxWorms = MIN(MAX_PLAYERS, MAX((int)inbs.readByte(), 0));
2398 	                nState = inbs.readByte();
2399 
2400 					if (nState < 0)  {
2401 						bOldLxBug = true;
2402 					}
2403 
2404                     szMapName = inbs.readString(256);
2405 					// Adjust the map name
2406 					if (szMapName.find("levels/") == 0)
2407 						szMapName.erase(0,7); // Remove the path if present
2408 					szMapName = CMap::GetLevelName(szMapName);
2409 
2410 
2411                     szModName = inbs.readString(256);
2412 	                nGameMode = inbs.readByte();
2413 	                nLives = inbs.readInt16();
2414 	                nMaxKills = inbs.readInt16();
2415 	                nLoadingTime = inbs.readInt16();
2416 					if(nLoadingTime < 0)  {
2417 						bOldLxBug = true;
2418 					}
2419                     nBonuses = inbs.readByte();
2420 
2421                     // Worms
2422                     nNumPlayers = inbs.readByte();
2423 					if (nNumPlayers < 0)  {
2424 						bOldLxBug = true;
2425 					}
2426 
2427 					// Check
2428 					nNumPlayers = MIN(nMaxWorms,nNumPlayers);
2429 
2430 					int i;
2431                     for(i=0; i<nNumPlayers; i++) {
2432                         cWorms[i].setName(inbs.readString());
2433                         cWorms[i].setKills(inbs.readInt(2));
2434                     }
2435 
2436 					if (nState == 1 && !bOldLxBug)  { // Loading and no bug? Must be a fixed version -> LXP/OLX b1 or 2
2437 						sServerVersion = "LXP or OLX beta1/beta2";
2438 					}
2439 
2440 					// Lives (only OLX servers)
2441 					if(!inbs.isPosAtEnd())  {
2442 						sServerVersion = "OpenLieroX/0.57_Beta3";
2443 						bHaveLives = true;
2444 						for(i=0; i<nNumPlayers; i++)
2445 							cWorms[i].setLives(inbs.readInt(2));
2446 					}
2447 
2448 					// IPs
2449 					if(!inbs.isPosAtEnd())  {
2450 						sServerVersion = "OpenLieroX/0.57_Beta4";
2451 						for(i=0; i<nNumPlayers; i++)
2452 							inbs.readString(); // ignore
2453 					}
2454 
2455 					if(!inbs.isPosAtEnd())  {
2456 						sServerVersion = inbs.readString();
2457 					}
2458 
2459 					if(!inbs.isPosAtEnd())  {
2460 						bHaveGameSpeed = true;
2461 						fGameSpeed = inbs.readFloat();
2462 					}
2463 
2464 					// since Beta9
2465 					if(!inbs.isPosAtEnd())  {
2466 						int ftC = inbs.readInt(2);
2467 						for(int i = 0; i < ftC; ++i) {
2468 							std::string name = inbs.readString();
2469 							std::string humanName = inbs.readString();
2470 							ScriptVar_t value; inbs.readVar(value);
2471 							bool olderClientsSupported = inbs.readBool();
2472 							Feature* f = featureByName(name);
2473 							if(f) {
2474 								features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_SUPPORTED);
2475 							} else if(!olderClientsSupported) {
2476 								features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_JUSTUNKNOWN);
2477 							} else {
2478 								features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_INCOMPATIBLE);
2479 							}
2480 						}
2481 					}
2482 
2483                 }
2484             }
2485         }
2486 
2487         if((tLX->currentTime - fStart > 1) && !bGotDetails) {
2488             nTries++;
2489             fStart = tLX->currentTime;
2490 			bGotDetails = false;
2491 			bOldLxBug = false;
2492 
2493 			if(svr)
2494 				Menu_SvrList_GetServerInfo(svr);
2495         }
2496 
2497 		// Got details, fill in the listview
2498 		if (bGotDetails && !bOldLxBug)  {
2499 
2500 			// States and gamemodes
2501    			const std::string states[] = {"Open", "Loading", "Playing", "Unknown"};
2502 			const std::string gamemodes[] = {"Deathmatch","Team Deathmatch", "Tag", "Demolitions", "Unknown"};
2503 
2504 
2505 			// Checks
2506 			if (nState < 0 || nState > 2)
2507 				nState = 3;
2508 			if (nGameMode < 0 || nGameMode > 3)
2509 				nGameMode = 4;
2510 			nNumPlayers = MIN(nNumPlayers, MAX_WORMS-1);
2511 
2512 
2513 			// Setup the listview
2514 			lvInfo.Setup(0, x + 15, y+5, w - 30, h - 25);
2515 			lvInfo.setDrawBorder(false);
2516 			lvInfo.setRedrawMenu(false);
2517 			lvInfo.setShowSelect(false);
2518 			lvInfo.setOldStyle(true);
2519 			// TODO: will the listview have scrollbars if too long? if not, please add this
2520 
2521 			lvInfo.Destroy(); // Clear any old info
2522 
2523 			// Columns
2524 			int first_column_width = tLX->cFont.GetWidth("Loading Times:") + 30; // Width of the widest item in this column + some space
2525 			int last_column_width = tLX->cFont.GetWidth("999"); // Kills width
2526 			lvInfo.AddColumn("", first_column_width);
2527 			lvInfo.AddColumn("", lvInfo.getWidth() - first_column_width - (last_column_width*2) - gfxGUI.bmpScrollbar.get()->w); // The rest
2528 			lvInfo.AddColumn("", last_column_width);
2529 			lvInfo.AddColumn("", last_column_width);
2530 
2531 			int index = 0;  // Current item index
2532 
2533 			// Server name
2534 			lvInfo.AddItem("servername", index, tLX->clNormalLabel);
2535 			lvInfo.AddSubitem(LVS_TEXT, "Server name:", (DynDrawIntf*)NULL, NULL);
2536 			lvInfo.AddSubitem(LVS_TEXT, szName, (DynDrawIntf*)NULL, NULL);
2537 
2538 			// server version
2539 			lvInfo.AddItem("serverversion", index, tLX->clNormalLabel);
2540 			lvInfo.AddSubitem(LVS_TEXT, "Server version:", (DynDrawIntf*)NULL, NULL);
2541 			lvInfo.AddSubitem(LVS_TEXT, sServerVersion, (DynDrawIntf*)NULL, NULL);
2542 
2543 			// Country and continent
2544 			lvInfo.AddItem("country", ++index, tLX->clNormalLabel);
2545 			lvInfo.AddSubitem(LVS_TEXT, "Location:", (DynDrawIntf*)NULL, NULL);
2546 			if (tIpInfo.hasCityLevel)
2547 				lvInfo.AddSubitem(LVS_TEXT, tIpInfo.city + ", " + tIpInfo.countryName + " (" + tIpInfo.continent + ")", (DynDrawIntf*)NULL, NULL);
2548 			else
2549 				lvInfo.AddSubitem(LVS_TEXT, tIpInfo.countryName + " (" + tIpInfo.continent + ")", (DynDrawIntf*)NULL, NULL);
2550 
2551 			// IP address
2552 			lvInfo.AddItem("ip", ++index, tLX->clNormalLabel);
2553 			lvInfo.AddSubitem(LVS_TEXT, "IP Address:", (DynDrawIntf*)NULL, NULL);
2554 			lvInfo.AddSubitem(LVS_TEXT, sIP, (DynDrawIntf*)NULL, NULL);
2555 
2556 			// Map name
2557 			lvInfo.AddItem("mapname", ++index, tLX->clNormalLabel);
2558 			lvInfo.AddSubitem(LVS_TEXT, "Level name:", (DynDrawIntf*)NULL, NULL);
2559 			lvInfo.AddSubitem(LVS_TEXT, szMapName, (DynDrawIntf*)NULL, NULL);
2560 
2561 			// Mod name
2562 			lvInfo.AddItem("modname", ++index, tLX->clNormalLabel);
2563 			lvInfo.AddSubitem(LVS_TEXT, "Mod name:", (DynDrawIntf*)NULL, NULL);
2564 			lvInfo.AddSubitem(LVS_TEXT, szModName, (DynDrawIntf*)NULL, NULL);
2565 
2566 			// State
2567 			lvInfo.AddItem("state", ++index, tLX->clNormalLabel);
2568 			lvInfo.AddSubitem(LVS_TEXT, "State:", (DynDrawIntf*)NULL, NULL);
2569 			lvInfo.AddSubitem(LVS_TEXT, states[nState], (DynDrawIntf*)NULL, NULL);
2570 
2571 			// Playing
2572 			lvInfo.AddItem("playing", ++index, tLX->clNormalLabel);
2573 			lvInfo.AddSubitem(LVS_TEXT, "Playing:", (DynDrawIntf*)NULL, NULL);
2574 			lvInfo.AddSubitem(LVS_TEXT, itoa(nNumPlayers) + " / " + itoa(nMaxWorms), (DynDrawIntf*)NULL, NULL);
2575 
2576 			// Game type
2577 			lvInfo.AddItem("game type", ++index, tLX->clNormalLabel);
2578 			lvInfo.AddSubitem(LVS_TEXT, "Game Type:", (DynDrawIntf*)NULL, NULL);
2579 			lvInfo.AddSubitem(LVS_TEXT, gamemodes[nGameMode], (DynDrawIntf*)NULL, NULL);
2580 
2581 			// Lives
2582 			lvInfo.AddItem("lives", ++index, tLX->clNormalLabel);
2583 			lvInfo.AddSubitem(LVS_TEXT, "Lives:", (DynDrawIntf*)NULL, NULL);
2584 			if (nLives < 0)
2585 				lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2586 			else
2587 				lvInfo.AddSubitem(LVS_TEXT, itoa(nLives), (DynDrawIntf*)NULL, NULL);
2588 
2589 			// Max kills
2590 			lvInfo.AddItem("maxkills", ++index, tLX->clNormalLabel);
2591 			lvInfo.AddSubitem(LVS_TEXT, "Max Kills:", (DynDrawIntf*)NULL, NULL);
2592 			if (nMaxKills < 0)
2593 				lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2594 			else
2595 				lvInfo.AddSubitem(LVS_TEXT, itoa(nMaxKills), (DynDrawIntf*)NULL, NULL);
2596 
2597 			// Loading times
2598 			lvInfo.AddItem("loading", ++index, tLX->clNormalLabel);
2599 			lvInfo.AddSubitem(LVS_TEXT, "Loading Times:", (DynDrawIntf*)NULL, NULL);
2600 			lvInfo.AddSubitem(LVS_TEXT, itoa(nLoadingTime) + " %", (DynDrawIntf*)NULL, NULL);
2601 
2602 			// Separator
2603 			lvInfo.AddItem("", ++index, tLX->clNormalLabel);
2604 
2605 			// Players / kills / lives
2606 			lvInfo.AddItem("players", ++index, tLX->clNormalLabel);
2607 			if (nState)  {
2608 				lvInfo.AddSubitem(LVS_TEXT, "Players/Kills/Lives:", (DynDrawIntf*)NULL, NULL);
2609 
2610 				// First player (located next to the Players/Kills/Lives label)
2611 				lvInfo.AddSubitem(LVS_TEXT, cWorms[0].getName(), (DynDrawIntf*)NULL, NULL);
2612 				lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[0].getKills()), (DynDrawIntf*)NULL, NULL);
2613 				if (bHaveLives)  {
2614 					switch ((short)cWorms[0].getLives())  {
2615 					case -1:  // Out
2616 						lvInfo.AddSubitem(LVS_TEXT, "Out", (DynDrawIntf*)NULL, NULL);
2617 						break;
2618 					case -2:  // Unlim
2619 						lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2620 						break;
2621 					default:
2622 						lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[0].getLives()), (DynDrawIntf*)NULL, NULL);
2623 					}
2624 				}
2625 
2626 				// Rest of the players
2627 				for (int i=1; i < nNumPlayers; i++)  {
2628 					lvInfo.AddItem("players"+itoa(i+1), ++index, tLX->clNormalLabel);
2629 					lvInfo.AddSubitem(LVS_TEXT, "", (DynDrawIntf*)NULL, NULL);
2630 					lvInfo.AddSubitem(LVS_TEXT, cWorms[i].getName(), (DynDrawIntf*)NULL, NULL);
2631 					lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[i].getKills()), (DynDrawIntf*)NULL, NULL);
2632 					if (bHaveLives)  {
2633 						switch ((short)cWorms[i].getLives())  {
2634 						case -1:  // Out
2635 							lvInfo.AddSubitem(LVS_TEXT, "Out", (DynDrawIntf*)NULL, NULL);
2636 							break;
2637 						case -2:  // Unlim
2638 							lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2639 							break;
2640 						default:
2641 							lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[i].getLives()), (DynDrawIntf*)NULL, NULL);
2642 						}
2643 					}
2644 				}
2645 			}
2646 
2647 			else  { // Don't draw kills when the server is open
2648 				lvInfo.AddSubitem(LVS_TEXT, "Players:", (DynDrawIntf*)NULL, NULL);
2649 
2650 				// First player (located next to the Players/Kills label)
2651 				lvInfo.AddSubitem(LVS_TEXT, cWorms[0].getName(), (DynDrawIntf*)NULL, NULL);
2652 
2653 				// Rest of the players
2654 				for (int i = 1; i < nNumPlayers; i++)  {
2655 					lvInfo.AddItem("players"+itoa(i+1), ++index, tLX->clNormalLabel);
2656 					lvInfo.AddSubitem(LVS_TEXT, "", (DynDrawIntf*)NULL, NULL);
2657 					lvInfo.AddSubitem(LVS_TEXT, cWorms[i].getName(), (DynDrawIntf*)NULL, NULL);
2658 				}
2659 			}
2660 
2661 			// Separator
2662 			lvInfo.AddItem("", ++index, tLX->clNormalLabel);
2663 
2664 			// Bonuses
2665 			lvInfo.AddItem("bonuses", ++index, tLX->clNormalLabel);
2666 			lvInfo.AddSubitem(LVS_TEXT, "Bonuses:", (DynDrawIntf*)NULL, NULL);
2667 			lvInfo.AddSubitem(LVS_TEXT, nBonuses ? "On" : "Off", (DynDrawIntf*)NULL, NULL);
2668 
2669 			if(bHaveGameSpeed) {
2670 				// Loading times
2671 				lvInfo.AddItem("gamespeed", ++index, tLX->clNormalLabel);
2672 				lvInfo.AddSubitem(LVS_TEXT, "Game speed:", (DynDrawIntf*)NULL, NULL);
2673 				lvInfo.AddSubitem(LVS_TEXT, ftoa(fGameSpeed), (DynDrawIntf*)NULL, NULL);
2674 			}
2675 
2676 			foreach( FeatureCompatibleSettingList::Feature&, it, features.list ){
2677 				Color col;
2678 				switch(it->get().type) {
2679 					case FeatureCompatibleSettingList::Feature::FCSL_JUSTUNKNOWN: col = tLX->clDisabled; break;
2680 					case FeatureCompatibleSettingList::Feature::FCSL_INCOMPATIBLE: col = tLX->clError; break;
2681 					default: col = tLX->clNormalLabel;
2682 				}
2683 				lvInfo.AddItem("feature:" + it->get().name, ++index, col);
2684 				if(tLX->cFont.GetWidth(it->get().humanName + ":") + 20 <= first_column_width) {
2685 					lvInfo.AddSubitem(LVS_TEXT, it->get().humanName + ":", (DynDrawIntf*)NULL, NULL);
2686 					lvInfo.AddSubitem(LVS_TEXT, it->get().var.toString(), (DynDrawIntf*)NULL, NULL);
2687 				} else
2688 					lvInfo.AddSubitem(LVS_TEXT, it->get().humanName + ":      " + it->get().var.toString(), (DynDrawIntf*)NULL, NULL);
2689 			}
2690 
2691 		}
2692 		else // No details yet
2693 			return;
2694     }
2695 
2696 	// No details, server down
2697     if(!bGotDetails) {
2698         tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2,y+tLX->cFont.GetHeight()+10, tLX->clError, "Unable to query server");
2699         return;
2700     }
2701 
2702 	// Old bug
2703     if(bOldLxBug) {
2704         tLX->cFont.Draw(VideoPostProcessor::videoSurface(), x+15,y+tLX->cFont.GetHeight()+10, tLX->clError, "You can't view details\nof this server because\nLieroX v0.56 contains a bug.\n\nPlease wait until the server\nchanges its state to Playing\nand try again.");
2705         return;
2706     }
2707 
2708 	// Process the listview events
2709 	mouse_t *Mouse = GetMouse();
2710 	if (lvInfo.InBox(Mouse->X, Mouse->Y))  {
2711 		lvInfo.MouseOver(Mouse);
2712 		if (Mouse->Down)
2713 			lvInfo.MouseDown(Mouse, true);
2714 		else if (Mouse->Up)
2715 			lvInfo.MouseUp(Mouse, false);
2716 
2717 		if (Mouse->WheelScrollUp)
2718 			lvInfo.MouseWheelUp(Mouse);
2719 		else if (Mouse->WheelScrollDown)
2720 			lvInfo.MouseWheelDown(Mouse);
2721 	}
2722 
2723 	// All ok, draw the details
2724 	lvInfo.Draw( VideoPostProcessor::videoSurface() );
2725 }
2726 
Menu_Current_Shutdown()2727 void Menu_Current_Shutdown() {
2728 	if(!tMenu) return;
2729 
2730 	// Shutdown all sub-menus
2731 	if(!bDedicated)
2732 		switch(tMenu->iMenuType) {
2733 
2734 				// Main
2735 			case MNU_MAIN:
2736 				Menu_MainShutdown();
2737 				break;
2738 
2739 				// Local
2740 			case MNU_LOCAL:
2741 				Menu_LocalShutdown();
2742 				break;
2743 
2744 				// News
2745 			case MNU_NETWORK:
2746 				Menu_NetShutdown();
2747 				break;
2748 
2749 				// Player
2750 			case MNU_PLAYER:
2751 				Menu_PlayerShutdown();
2752 				break;
2753 
2754 				// Map editor
2755 			case MNU_MAPED:
2756 				Menu_MapEdShutdown();
2757 				break;
2758 
2759 				// Options
2760 			case MNU_OPTIONS:
2761 				Menu_OptionsShutdown();
2762 				break;
2763 		}
2764 
2765 	/*Menu_MainShutdown();
2766 	Menu_LocalShutdown();
2767 	Menu_PlayerShutdown();
2768 	Menu_MapEdShutdown();
2769 	Menu_GameSettingsShutdown();
2770 	Menu_WeaponsRestrictionsShutdown();
2771 	Menu_WeaponPresetsShutdown();
2772 	Menu_BanListShutdown();
2773 	Menu_ServerSettingsShutdown();
2774 	Menu_OptionsShutdown();
2775 	Menu_FloatingOptionsShutdown();
2776 	Menu_SpeedTest_Shutdown();
2777 	Menu_NetShutdown();
2778 	Menu_Net_MainShutdown();
2779 	Menu_Net_HostPlyShutdown();
2780 	Menu_Net_HostLobbyShutdown();
2781 	Menu_Net_LANShutdown();
2782 	Menu_Net_JoinShutdown();
2783 	Menu_Net_FavouritesShutdown();
2784 	Menu_Net_NewsShutdown();
2785 	Menu_Net_JoinShutdown();
2786 	Menu_Net_ChatShutdown();
2787 	Menu_Net_JoinConnectionShutdown();
2788 	Menu_Net_JoinLobbyShutdown();
2789 	Menu_Net_NETShutdown();
2790 	Menu_CGuiSkinShutdown();*/
2791 }
2792 
2793 } // namespace DeprecatedGUI
2794