1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 // get our interface
14 #include "bzflag.h"
15 
16 /* system headers */
17 #include <assert.h>
18 #include <ctype.h>
19 #include <fstream>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <string>
25 #include <sys/types.h>
26 #include <time.h>
27 #include <vector>
28 #if defined(_WIN32)
29 #  include <shlobj.h>
30 #  include <sys/types.h>
31 #  include <sys/stat.h>
32 #  include <direct.h>
33 #else
34 #  include <pwd.h>
35 #  include <dirent.h>
36 #endif /* defined(_WIN32) */
37 
38 /* common headers */
39 #include "Address.h"
40 #include "AresHandler.h"
41 #include "BZDBCache.h"
42 #include "BZDBLocal.h"
43 #include "BundleMgr.h"
44 #include "BzfMedia.h"
45 #include "BzfVisual.h"
46 #include "BzfWindow.h"
47 #include "CommandManager.h"
48 #include "CommandsStandard.h"
49 #include "ConfigFileManager.h"
50 #include "DirectoryNames.h"
51 #include "ErrorHandler.h"
52 #include "FileManager.h"
53 #include "FontManager.h"
54 #include "GUIOptionsMenu.h"
55 #include "KeyManager.h"
56 #include "OSFile.h"
57 #include "OpenGLGState.h"
58 #include "PlatformFactory.h"
59 #include "Protocol.h"
60 #include "ServerListCache.h"
61 #include "StateDatabase.h"
62 #include "Team.h"
63 #include "TextUtils.h"
64 #include "TextureManager.h"
65 #include "WordFilter.h"
66 #include "World.h"
67 #include "bzfSDL.h"
68 #include "bzfgl.h"
69 #include "bzfio.h"
70 
71 /* local headers */
72 #include "ActionBinding.h"
73 #include "ServerStartMenu.h"
74 #include "callbacks.h"
75 #include "playing.h"
76 #include "sound.h"
77 #include "playing.h"
78 
79 // invoke incessant rebuilding for build versioning
80 #include "version.h"
81 
82 // defaults for bzdb
83 #include "defaultBZDB.h"
84 
85 // client prefrences
86 #include "clientConfig.h"
87 
88 int beginendCount = 0;
89 
90 const char*     argv0;
91 std::string     alternateConfig;
92 static bool     noAudio = false;
93 struct tm       userTime;
94 bool            echoToConsole = false;
95 bool            echoAnsi = false;
96 int         debugLevel = 0;
97 
98 static BzfDisplay*  display = NULL;
99 
100 
101 
102 #ifdef ROBOT
103 // ROBOT -- tidy up
104 int numRobotTanks = 0;
105 #endif
106 
107 
108 //
109 // application initialization
110 //
111 
112 // so that Windows can kill the wsa stuff if needed
bail(int returnCode)113 int bail ( int returnCode )
114 {
115 #ifdef _WIN32
116     WSACleanup();
117 #endif
118     return returnCode;
119 }
120 
setVisual(BzfVisual * visual)121 static void     setVisual(BzfVisual* visual)
122 {
123     // sine qua non
124     visual->setLevel(0);
125     visual->setDoubleBuffer(true);
126     visual->setRGBA(1, 1, 1, 0);
127 
128     // ask for a zbuffer if not disabled.  we might
129     // choose not to use it if we do ask for it.
130     if (!BZDB.isSet("zbuffer") || (BZDB.get("zbuffer") != "disable"))
131     {
132         int depthLevel = 16;
133         if (BZDB.isSet("forceDepthBits"))
134             depthLevel = BZDB.evalInt("forceDepthBits");
135         visual->setDepth(depthLevel);
136     }
137 
138     // optional
139     visual->setStencil(4);
140 #if defined(DEBUG_RENDERING)
141     visual->setStencil(4);
142 #else
143     visual->setStencil(1);
144 #endif
145 #ifdef USE_GL_STEREO
146     if (BZDB.isSet("view") && BZDB.get("view") == configViewValues[1])
147         visual->setStereo(true);
148 #endif
149     visual->setVerticalSync(BZDB.evalInt("saveEnergy") == 2);
150 }
151 
usage()152 static void     usage()
153 {
154     printFatalError("usage: %s"
155                     " [-badwords <filterfile>]"
156                     " [-config <configfile>]"
157                     " [-configdir <config dir name>]"
158                     " [-d | -debug]"
159 #ifdef DEBUG
160                     " [-date mm/dd/yyyy]"
161 #endif
162                     " [{-dir | -directory} <data-directory>]"
163                     " [-e | -echo]"
164                     " [-ea | -echoAnsi]"
165                     " [-eyesep separation]"
166                     " [-focal distance]"
167                     " [-h | -help | --help]"
168                     " [-latitude <latitude>] [-longitude <longitude>]"
169                     " [-list <list-server-url>] [-nolist]"
170                     " [-locale <locale>]"
171                     " [-m | -mute]"
172                     " [-motd <motd-url>] [-nomotd]"
173                     " [-multisample]"
174 #ifdef ROBOT
175                     " [-solo <num-robots>]"
176 #endif
177                     " [-team {red|green|blue|purple|rogue|observer}]"
178                     " [-time hh:mm:ss] [-notime]"
179                     " [-v | -version | --version]"
180                     " [-view {normal|stereo|stacked|three|anaglyph|interlaced}]"
181                     " [-window <geometry-spec>]"
182                     " [-zbuffer {on|off}]"
183                     " [callsign[:password]@]server[:port]\n\nExiting.", argv0);
184     if (display != NULL)
185     {
186         delete display;
187         display=NULL;
188     }
189     exit(1);
190 }
191 
checkArgc(int & i,int argc,const char * option,const char * type="Missing")192 static void checkArgc(int& i, int argc, const char* option, const char *type = "Missing")
193 {
194     if ((i+1) == argc)
195     {
196         printFatalError("%s argument for %s\n", type, option);
197         usage();
198     }
199     i++; // just skip the option argument string
200 }
201 
parse(int argc,char ** argv)202 static void     parse(int argc, char** argv)
203 {
204 // = 9;
205     for (int i = 1; i < argc; i++)
206     {
207         if (strcmp(argv[i], "-config") == 0)
208         {
209             checkArgc(i, argc, argv[i]);
210             // the setting has already been done in parseConfigName()
211         }
212         else if (strcmp(argv[i], "-configdir") == 0)
213         {
214             checkArgc(i, argc, argv[i]);
215             // the setting has already been done in parseConfigName()
216         }
217         else if ((strcmp(argv[i], "-dir") == 0) ||
218                  (strcmp(argv[i], "-directory") == 0))
219         {
220             checkArgc(i, argc, argv[i]);
221             if (strlen(argv[i]) == 0)
222                 BZDB.unset("directory");
223             else
224                 BZDB.set("directory", argv[i]);
225         }
226         else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "-echo") == 0)
227             echoToConsole = true;
228         else if (strcmp(argv[i], "-ea") == 0 || strcmp(argv[i], "-echoAnsi") == 0)
229         {
230             echoToConsole = true;
231             echoAnsi = true;
232         }
233         else if (strcmp(argv[i], "-h") == 0 ||
234                  strcmp(argv[i], "-help") == 0 ||
235                  strcmp(argv[i], "--help") == 0)
236             usage();
237         else if (strcmp(argv[i], "-latitude") == 0)
238         {
239             checkArgc(i, argc, argv[i]);
240             double latitude = atof(argv[i]);
241             if (latitude < -90.0 || latitude > 90.0)
242             {
243                 printFatalError("Invalid argument for %s.", argv[i-1]);
244                 usage();
245             }
246             BZDB.set("latitude", argv[i]);
247         }
248         else if (strcmp(argv[i], "-longitude") == 0)
249         {
250             checkArgc(i, argc, argv[i]);
251             double longitude = atof(argv[i]);
252             if (longitude < -180.0 || longitude > 180.0)
253             {
254                 printFatalError("Invalid argument for %s.", argv[i-1]);
255                 usage();
256             }
257             BZDB.set("longitude", argv[i]);
258         }
259         else if (strcmp(argv[i], "-list") == 0)
260         {
261             checkArgc(i, argc, argv[i]);
262             if (strcmp(argv[i], "default") == 0)
263                 BZDB.set("list", BZDB.getDefault("list"));
264             else
265             {
266                 startupInfo.listServerURL = argv[i];
267                 BZDB.set("list", argv[i]);
268             }
269         }
270         else if (strcmp(argv[i], "-locale") == 0)
271         {
272             checkArgc(i, argc, argv[i]);
273             BZDB.set("locale", argv[i]);
274         }
275         else if (strcmp(argv[i], "-motd") == 0)
276         {
277             checkArgc(i, argc, argv[i]);
278             if (strcmp(argv[i], "default") == 0)
279                 BZDB.set("motdServer", BZDB.getDefault("motdServer"));
280             else
281                 BZDB.set("motdServer", argv[i]);
282             BZDB.unset("disableMOTD");
283         }
284         else if (strcmp(argv[i], "-nomotd") == 0)
285             BZDB.set("disableMOTD", "1");
286         else if (strcmp(argv[i], "-nolist") == 0)
287         {
288             startupInfo.listServerURL = "";
289             BZDB.set("list", "");
290         }
291         else if (strcmp(argv[i], "-m") == 0 ||
292                  strcmp(argv[i], "-mute") == 0)
293             noAudio = true;
294         else if (strcmp(argv[i], "-multisample") == 0)
295         {
296             BZDB.set("_multisample", "1");
297 #ifdef ROBOT
298         }
299         else if (strcmp(argv[i], "-solo") == 0)
300         {
301             checkArgc(i, argc, argv[i]);
302             numRobotTanks = atoi(argv[i]);
303             if (numRobotTanks < 1 || numRobotTanks > MAX_ROBOTS)
304             {
305                 printFatalError("Invalid argument for %s.", argv[i-1]);
306                 usage();
307             }
308 #endif
309         }
310         else if (strcmp(argv[i], "-team") == 0)
311         {
312             checkArgc(i, argc, argv[i]);
313             if ((strcmp(argv[i], "a") == 0) ||
314                     (strcmp(argv[i], "auto") == 0) ||
315                     (strcmp(argv[i], "automatic") == 0))
316                 startupInfo.team = AutomaticTeam;
317             else if (strcmp(argv[i], "r") == 0 || strcmp(argv[i], "red") == 0)
318                 startupInfo.team = RedTeam;
319             else if (strcmp(argv[i], "g") == 0 || strcmp(argv[i], "green") == 0)
320                 startupInfo.team = GreenTeam;
321             else if (strcmp(argv[i], "b") == 0 || strcmp(argv[i], "blue") == 0)
322                 startupInfo.team = BlueTeam;
323             else if (strcmp(argv[i], "p") == 0 || strcmp(argv[i], "purple") == 0)
324                 startupInfo.team = PurpleTeam;
325             else if (strcmp(argv[i], "z") == 0 || strcmp(argv[i], "rogue") == 0)
326                 startupInfo.team = RogueTeam;
327             else if (strcmp(argv[i], "o") == 0 || strcmp(argv[i], "observer") == 0)
328                 startupInfo.team = ObserverTeam;
329             else
330             {
331                 printFatalError("Invalid argument for %s.", argv[i-1]);
332                 usage();
333             }
334         }
335         else if (strcmp(argv[i], "-v") == 0 ||
336                  strcmp(argv[i], "-version") == 0 ||
337                  strcmp(argv[i], "--version") == 0)
338         {
339             printFatalError("BZFlag client %s (protocol %s) http://BZFlag.org/\n%s",
340                             getAppVersion(),
341                             getProtocolVersion(),
342                             bzfcopyright);
343             bail(0);
344             exit(0);
345         }
346         else if (strcmp(argv[i], "-window") == 0)
347         {
348             BZDB.set("_window", "1");
349             checkArgc(i, argc, argv[i]);
350             int w, h, x, y, count;
351             char xs ='+', ys='+';
352             if (strcmp(argv[i], "default") != 0
353                     && (((count = sscanf(argv[i], "%dx%d%c%d%c%d", &w, &h, &xs, &x, &ys, &y)) != 6 && count != 2)
354                         || (xs != '-' && xs != '+')
355                         || (ys != '-' && ys != '+')))
356             {
357                 printFatalError("Invalid argument for %s. \nCorrect format is <width>x<height>[+|-]<x>[+|-].", argv[i-1]);
358                 usage();
359             }
360             BZDB.set("geometry", argv[i]);
361 #ifdef DEBUG
362         }
363         else if (strcmp(argv[i], "-date") == 0)
364         {
365             checkArgc(i, argc, argv[i]);
366             int month, day, year;
367             // FIXME: should use iso yyyy.mm.dd format
368             if (sscanf(argv[i], "%d/%d/%d", &month, &day, &year) != 3 ||
369                     day < 1 || day > 31 ||      // FIXME -- upper limit loose
370                     month < 1 || month > 12 ||
371                     (year < 0 || (year > 100 && (year < 1970 || year > 2100))))
372             {
373                 printFatalError("Invalid argument for %s.", argv[i-1]);
374                 usage();
375             }
376             if (year > 100) year = year - 1900;
377             else if (year < 70) year += 100;
378             userTime.tm_mday = day;
379             userTime.tm_mon = month - 1;
380             userTime.tm_year = year;
381 #endif
382         }
383         else if (strcmp(argv[i], "-time") == 0)
384         {
385             checkArgc(i, argc, argv[i]);
386             BZDB.set("fixedTime", argv[i]);
387         }
388         else if (strcmp(argv[i], "-notime") == 0)
389             BZDB.unset("fixedTime");
390         else if (strcmp(argv[i], "-view") == 0)
391         {
392             checkArgc(i, argc, argv[i]);
393             BZDB.set("view", argv[i]);
394         }
395         else if (strcmp(argv[i], "-zbuffer") == 0)
396         {
397             checkArgc(i, argc, argv[i]);
398             if (strcmp(argv[i], "on") == 0)
399                 BZDB.set("zbuffer", "1");
400             else if (strcmp(argv[i], "off") == 0)
401                 BZDB.set("zbuffer", "disable");
402             else
403             {
404                 printFatalError("Invalid argument for %s.", argv[i-1]);
405                 usage();
406             }
407         }
408         else if (strcmp(argv[i], "-eyesep") == 0)
409         {
410             checkArgc(i, argc, argv[i]);
411             BZDB.set("eyesep", argv[i]);
412         }
413         else if (strcmp(argv[i], "-focal") == 0)
414         {
415             checkArgc(i, argc, argv[i]);
416             BZDB.set("focal", argv[i]);
417         }
418         else if (strncmp(argv[i], "-psn", 4) == 0)
419         {
420             std::vector<std::string> args;
421             args.push_back(argv[i]);
422             printError("Ignoring Finder argument \"{1}\"", &args);
423             // ignore process serial number argument (-psn_x_xxxx for MacOS X
424         }
425         else if (strcmp(argv[i], "-badwords") == 0)
426         {
427             checkArgc(i, argc, argv[i], "Missing bad word filter file");
428             BZDB.set("filterFilename", argv[i], StateDatabase::ReadOnly);
429         }
430         else if (argv[i][0] != '-')
431         {
432             if (i == (argc - 1))
433             {
434                 // argv[i] = username:password@server:port
435                 // variables to store
436                 std::string serverName, callsign, password;
437                 int port;
438 
439                 // start splitting stuff
440                 const std::string argument = std::string(argv[i]);
441                 const size_t atSplit = argument.find("@");
442                 const size_t portSplit = argument.rfind(":");
443                 const size_t passSplit = argument.find(":");
444 
445                 if (atSplit != std::string::npos)   // we found an "@"
446                 {
447                     if (portSplit != std::string::npos)   // we have a port
448                     {
449                         serverName = argument.substr(atSplit + 1, portSplit - atSplit - 1);
450                         port = atoi(argument.substr(portSplit + 1, argument.length() - portSplit).c_str());
451 
452                         if (port < 1 || port > 65535)   // invalid port
453                         {
454                             printFatalError("Bad port, using default %d.", ServerPort);
455                             port = ServerPort;
456                         }
457                     }
458                     else   //we don't have a port
459                     {
460                         serverName = argument.substr(atSplit + 1, argument.length() - atSplit);
461                         port = ServerPort;
462                     }
463 
464                     if (portSplit != passSplit)   // there's a password to parse
465                     {
466                         callsign = argument.substr(0, passSplit);
467                         password = argument.substr(passSplit + 1, atSplit - passSplit - 1);
468                     }
469                     else   // just a username
470                     {
471                         callsign = argument.substr(0, atSplit);
472                         password = "";
473                     }
474 
475                     // length checks and always truncate everything after the max length
476                     if (callsign.length() > sizeof(startupInfo.callsign))
477                     {
478                         callsign.erase(sizeof(startupInfo.callsign) - 1, std::string::npos);
479                         printFatalError("Callsign truncated after %d characters.", sizeof(startupInfo.callsign));
480                     }
481                     if (password.length() > sizeof(startupInfo.password))
482                     {
483                         password.erase(sizeof(startupInfo.password) - 1, std::string::npos);
484                         printFatalError("Password truncated after %d characters.", sizeof(startupInfo.password));
485                     }
486                     if (serverName.length() > sizeof(startupInfo.serverName))
487                     {
488                         serverName.erase(sizeof(startupInfo.serverName) - 1, std::string::npos);
489                         printFatalError("Server name truncated after %d characters.", sizeof(startupInfo.serverName));
490                     }
491 
492                     // assign variables with strcpy because char[] can't be assigned
493                     strcpy(startupInfo.callsign, callsign.c_str());
494                     strcpy(startupInfo.password, password.c_str());
495                     strcpy(startupInfo.serverName, serverName.c_str());
496                     startupInfo.serverPort = port;
497                 }
498                 else   // there is no callsign/password so only a destination
499                 {
500                     if (portSplit != std::string::npos)   // we have a port
501                     {
502                         serverName = argument.substr(atSplit + 1, portSplit - atSplit - 1);
503                         port = atoi(argument.substr(portSplit + 1, argument.length() - portSplit).c_str());
504 
505                         if (port < 1 || port > 65535)   // invalid port
506                         {
507                             printFatalError("Bad port, using default %d.", ServerPort);
508                             port = ServerPort;
509                         }
510                     }
511                     else   //we don't have a port
512                     {
513                         serverName = argument.substr(atSplit + 1, argument.length() - atSplit);
514                         port = ServerPort;
515                     }
516 
517                     // sanity check for length
518                     if (serverName.length() > sizeof(startupInfo.serverName))
519                     {
520                         serverName.erase(sizeof(startupInfo.serverName) - 1, std::string::npos);
521                         printFatalError("Server name truncated after %d characters.", sizeof(startupInfo.serverName));
522                     }
523 
524                     strcpy(startupInfo.serverName, serverName.c_str());
525                     startupInfo.serverPort = port;
526                 }
527 
528                 startupInfo.autoConnect = true; // automatically connect on start up
529             }
530             else
531                 printFatalError("Unexpected: %s. Server must go after all options.", argv[i]);
532         }
533         else if (strcmp(argv[i], "-debug") == 0)
534             debugLevel++;
535 #ifdef __APPLE__
536         else if (strcmp(argv[i], "-NSDocumentRevisionsDebugMode") == 0)
537             // ignore this option that is appended when running from inside Xcode
538             checkArgc(i, argc, argv[i]);
539 #endif // __APPLE__
540         // has to be the last option that starts with -d
541         else if (strncmp(argv[i], "-d", 2) == 0)
542         {
543             const char num = argv[i][2];
544             if ((num >= '0') && (num <= '9') && (argv[i][3] == 0))
545                 debugLevel = num - '0';
546             else
547             {
548                 const char* c = argv[i] + 2;
549                 while (*c != 0)
550                 {
551                     if (*c != 'd')
552                     {
553                         printFatalError("Unknown option %s.", argv[i]);
554                         usage();
555                     }
556                     c++;
557                 }
558                 debugLevel += (int)((c - argv[i]) - 1);
559             }
560         }
561         else
562         {
563             printFatalError("Unknown option %s.", argv[i]);
564             usage();
565         }
566     }
567 }
568 
parseConfigName(int argc,char ** argv)569 static void parseConfigName(int argc, char** argv)
570 {
571     for (int i = 1; i < argc; i++)
572     {
573         if (strcmp(argv[i], "-configdir") == 0)
574         {
575             checkArgc(i, argc, argv[i]);
576             setCustomConfigDir(argv[i]);
577         }
578     }
579     for (int i = 1; i < argc; i++)
580     {
581         if (strcmp(argv[i], "-config") == 0)
582         {
583             checkArgc(i, argc, argv[i]);
584             alternateConfig = getConfigDirName();
585             alternateConfig += argv[i];
586         }
587     }
588 }
589 
590 //
591 // resource database dumping.  used during initial startup to save
592 // preferences in case anything catastrophic goes wrong afterwards
593 // (so user won't have to wait through performance testing again).
594 //
595 
dumpResources()596 void dumpResources()
597 {
598     // collect new configuration
599 
600     // only dump username and password if we're allowed to, otherwise
601     // erase them if they exist
602     if (BZDB.eval("saveIdentity") > 0)
603         BZDB.set("callsign", startupInfo.callsign);
604     else
605         BZDB.set("callsign", "");
606     if (BZDB.eval("saveIdentity") > 1)
607         BZDB.set("password", startupInfo.password);
608     else
609         BZDB.set("password", "");
610 
611     BZDB.set("team", Team::getName(startupInfo.team));
612     BZDB.set("server", startupInfo.serverName);
613     if (startupInfo.serverPort != ServerPort)
614         BZDB.set("port", TextUtils::format("%d", startupInfo.serverPort));
615     else
616         BZDB.unset("port");
617     BZDB.set("list", startupInfo.listServerURL);
618     if (isSoundOpen())
619         BZDB.set("volume", TextUtils::format("%d", getSoundVolume()));
620 
621     if (RENDERER.getWindow().getWindow()->hasGammaControl())
622     {
623         BZDB.set("gamma",
624                  TextUtils::format("%f", RENDERER.getWindow().getWindow()->getGamma()));
625     }
626 
627     BZDB.set("quality", configQualityValues[RENDERER.useQuality()]);
628     if (!BZDB.isSet("_window") && display->getResolution() != -1 &&
629             display->getResolution(display->getResolution()))
630         BZDB.set("resolution", display->getResolution(display->getResolution())->name);
631     BZDB.set("startcode", ServerStartMenu::getSettings());
632 
633     BZDB.set("panelopacity", TextUtils::format("%f", RENDERER.getPanelOpacity()));
634 
635     BZDB.set("radaropacity", TextUtils::format("%f", RENDERER.getRadarOpacity()));
636 
637     BZDB.set("mouseboxsize", TextUtils::format("%d", RENDERER.getMaxMotionFactor()));
638 
639     // don't save these configurations
640     BZDB.setPersistent("_window", false);
641     BZDB.setPersistent("_multisample", false);
642 
643     const std::vector<std::string> list = getSilenceList();
644 
645     // add entries silencedPerson0 silencedPerson1 etc..
646     // to the database. Stores silenceList
647     // By only allowing up to a certain # of people can prevent
648     // the vague chance of buffer overrun.
649     const int bufferLength = 30;
650     int maxListSize = 1000000; //do even that many play bzflag?
651     char buffer [bufferLength];
652 
653     if ((int)list.size() < maxListSize) maxListSize = list.size();
654     for (int i = 0; i < maxListSize; i++)
655     {
656         sprintf(buffer, "silencedPerson%d", i);
657         BZDB.set(TextUtils::format("silencedPerson%d", i), list[i]);
658     }
659 
660     BZDB.set("motto", startupInfo.motto);
661 
662     BZDB.set("serverCacheAge", TextUtils::format("%1d", (long)(ServerListCache::get())->getMaxCacheAge()));
663 
664     (ServerListCache::get())->saveCache();
665 }
666 
needsFullscreen()667 static bool     needsFullscreen()
668 {
669     // fullscreen if not in a window
670     if (!BZDB.isSet("_window")) return true;
671 
672     // not fullscreen if view is default (normal)
673     if (!BZDB.isSet("view")) return false;
674 
675     // fullscreen if view is not default
676     std::string value = BZDB.get("view");
677     for (int i = 1; i < (int)configViewValues.size(); i++)
678         if (value == configViewValues[i])
679             return true;
680 
681     // bogus view, default to normal so no fullscreen
682     return false;
683 }
684 
createCacheSignature()685 static void createCacheSignature ()
686 {
687     // This file is to be used by archiving and mirroring tools avoid
688     // this directory (and any of its sub-directories). Please see:
689     //     < http://www.brynosaurus.com/cachedir/ >
690 
691     const char cacheSignature[] = "Signature: 8a477f597d28d172789f06886806bc55\n";
692     const char cacheComment[] =
693         "# This file is a cache directory tag created by BZFlag.\n"
694         "# For information about cache directory tags, see:\n"
695         "#     http://www.brynosaurus.com/cachedir/\n";
696     std::string cacheTagName =  getCacheDirName();
697     cacheTagName += "CACHEDIR.TAG";
698     std::ostream* cacheTag = FILEMGR.createDataOutStream(cacheTagName, true, true);
699     if (cacheTag != NULL)
700     {
701         cacheTag->write(cacheSignature, strlen(cacheSignature)); // Flawfinder: ignore
702         cacheTag->write(cacheComment, strlen(cacheComment)); // Flawfinder: ignore
703     }
704     delete cacheTag;
705 
706     return;
707 }
708 
709 
710 //
711 // main()
712 //  initialize application and enter event loop
713 //
714 
715 #if defined(_WIN32) && !defined(HAVE_SDL)
myMain(int argc,char ** argv)716 int         myMain(int argc, char** argv)
717 #else /* defined(_WIN32) */
718 int         main(int argc, char** argv)
719 #endif /* defined(_WIN32) */
720 {
721 #ifdef _WIN32
722     // startup winsock
723     static const int major = 2, minor = 2;
724     WSADATA wsaData;
725     if (WSAStartup(MAKEWORD(major, minor), &wsaData))
726     {
727         printFatalError("Failed to initialize winsock.  Terminating.\n");
728         return 1;
729     }
730     if (LOBYTE(wsaData.wVersion) != major || HIBYTE(wsaData.wVersion) != minor)
731     {
732         printFatalError("Version mismatch in winsock;"
733                         "  got %d.%d, expected %d.%d.  Terminating.\n",
734                         (int)LOBYTE(wsaData.wVersion),
735                         (int)HIBYTE(wsaData.wVersion),
736                         major, minor);
737         return bail(1);
738     }
739 #endif
740 
741     initGlobalAres();
742 
743     WordFilter *filter = (WordFilter *)NULL;
744 
745     argv0 = argv[0];
746 
747     // init libs
748 
749     //init_packetcompression();
750 
751     // initialize global objects and classes
752     bzfsrand((unsigned int)time(0));
753 
754     // set default DB entries
755     for (unsigned int gi = 0; gi < numGlobalDBItems; ++gi)
756     {
757         assert(globalDBItems[gi].name != NULL);
758         if (globalDBItems[gi].value != NULL)
759         {
760             BZDB.set(globalDBItems[gi].name, globalDBItems[gi].value);
761             BZDB.setDefault(globalDBItems[gi].name, globalDBItems[gi].value);
762         }
763         BZDB.setPersistent(globalDBItems[gi].name, globalDBItems[gi].persistent);
764         BZDB.setPermission(globalDBItems[gi].name, globalDBItems[gi].permission);
765     }
766 
767     BZDBCache::init();
768     BZDBLOCAL.init();
769 
770     Flags::init();
771 
772     time_t timeNow;
773     time(&timeNow);
774     userTime = *localtime(&timeNow);
775 
776     CommandsStandard::add();
777     unsigned int i;
778 
779     initConfigData();
780     loadBZDBDefaults();
781 
782     // parse for the config filename
783     // the rest of the options are parsed after the config file
784     // has been loaded to allow for command line overrides
785     parseConfigName(argc, argv);
786 
787     // read resources
788     if (alternateConfig != "")
789     {
790         if (CFGMGR.read(alternateConfig))
791             startupInfo.hasConfiguration = true;
792     }
793     if (!startupInfo.hasConfiguration)
794     {
795         findConfigFile();
796         if (CFGMGR.read(getCurrentConfigFileName()))
797         {
798             startupInfo.hasConfiguration = true;
799             updateConfigFile();
800         }
801     }
802 
803     // Create the cache signature
804     createCacheSignature();
805 
806     if (startupInfo.hasConfiguration)
807         ActionBinding::instance().getFromBindings();
808     else
809         // bind default keys
810         ActionBinding::instance().resetBindings();
811 
812     ServerListCache::get()->loadCache();
813 
814     // restore some configuration (command line overrides these)
815     if (startupInfo.hasConfiguration)
816     {
817         if (BZDB.isSet("callsign"))
818         {
819             // Flawfinder: ignore
820             strncpy(startupInfo.callsign, BZDB.get("callsign").c_str(),
821                     sizeof(startupInfo.callsign) - 1);
822             startupInfo.callsign[sizeof(startupInfo.callsign) - 1] = '\0';
823         }
824         if (BZDB.isSet("password"))
825         {
826             // Flawfinder: ignore
827             strncpy(startupInfo.password, BZDB.get("password").c_str(),
828                     sizeof(startupInfo.password) - 1);
829             startupInfo.password[sizeof(startupInfo.password) - 1] = '\0';
830         }
831 
832         if (BZDB.isSet("team"))
833         {
834             std::string value = BZDB.get("team");
835             startupInfo.team = Team::getTeam(value);
836         }
837         if (BZDB.isSet("server"))
838         {
839             // Flawfinder: ignore
840             strncpy(startupInfo.serverName, BZDB.get("server").c_str(),
841                     sizeof(startupInfo.serverName) - 1);
842             startupInfo.serverName[sizeof(startupInfo.serverName) - 1] = '\0';
843         }
844         if (BZDB.isSet("port"))
845             startupInfo.serverPort = atoi(BZDB.get("port").c_str());
846 
847         // ignore window name in config file (it's used internally)
848         BZDB.unset("_window");
849         BZDB.unset("_multisample");
850 
851 
852         // however, if the "__window" setting is enabled, let it through
853         if (BZDB.isSet("__window"))
854             if (BZDB.isTrue("__window"))
855                 BZDB.set("_window", "1");
856     }
857 
858     // use UDP? yes
859     startupInfo.useUDPconnection=true;
860 
861     // parse arguments
862     parse(argc, argv);
863 
864 #ifdef _WIN32
865     // this is cheap but it will work on windows
866     // clear out the stdout file
867     if (echoToConsole)
868     {
869         std::string filename = getConfigDirName() + "stdout.txt";
870         FILE *fp = fopen (filename.c_str(), "w");
871         if (fp)
872         {
873             fprintf(fp,"stdout started\r\n" );
874             fclose(fp);
875         }
876     }
877 #endif
878 
879     if (BZDB.isSet("directory"))
880     {
881         //Convert to unix paths so that escaping isn't an issue
882         std::string directory = BZDB.get("directory");
883         OSFileOSToStdDir(directory);
884         BZDB.set("directory", directory);
885     }
886 
887     if (debugLevel >= 4)
888         BZDB.setDebug(true);
889 
890     // set time from BZDB
891     if (BZDB.isSet("fixedTime"))
892     {
893         int hours, minutes, seconds;
894         char dbTime[256];
895         // Flawfinder: ignore
896         strncpy(dbTime, BZDB.get("fixedTime").c_str(), sizeof(dbTime) - 1);
897         dbTime[sizeof(dbTime) - 1] = '\0';
898         if (sscanf(dbTime, "%d:%d:%d", &hours, &minutes, &seconds) != 3 ||
899                 hours < 0 || hours > 23 ||
900                 minutes < 0 || minutes > 59 ||
901                 seconds < 0 || seconds > 59)
902             printFatalError("Invalid argument for fixedTime = %s", dbTime);
903         userTime.tm_sec = seconds;
904         userTime.tm_min = minutes;
905         userTime.tm_hour = hours;
906     }
907 
908     // see if there is a _default_ badwords file
909     if (!BZDB.isSet("filterFilename"))
910     {
911         std::string name;
912         name = getConfigDirName();
913         name += "badwords.txt";
914 
915         // get a handle on a filter object to attempt a load
916         if (BZDB.isSet("filter"))
917         {
918             filter = (WordFilter *)BZDB.getPointer("filter");
919             if (filter == NULL)
920                 filter = new WordFilter();
921         }
922         else
923         {
924             // filter is not set
925             filter = new WordFilter();
926         }
927         // XXX should stat the file first and load with interactive feedback
928         unsigned int count = filter->loadFromFile(name, false);
929         if (count > 0)
930             std::cout << "Loaded " << count << " words from \"" << name << "\"" << std::endl;
931     }
932 
933     // load the bad word filter, regardless of a default, if it was set
934     if (BZDB.isSet("filterFilename"))
935     {
936         std::string filterFilename = BZDB.get("filterFilename");
937         std::cout << "Filter file name specified is \"" << filterFilename << "\"" << std::endl;
938         if (filterFilename.length() != 0)
939         {
940             if (filter == NULL)
941                 filter = new WordFilter();
942             std::cout << "Loading " << filterFilename << std::endl;
943             unsigned int count = filter->loadFromFile(filterFilename, true);
944             std::cout << "Loaded " << count << " words" << std::endl;
945 
946             // stash the filter into the database for retrieval later
947             BZDB.setPointer("filter", (void *)filter, StateDatabase::ReadOnly );
948             BZDB.setPersistent("filter", false);
949         }
950         else
951             std::cerr << "WARNING: A proper file name was not given for the -badwords argument" << std::endl;
952     }
953 
954     // use empty motto string unless previously set
955     std::string motto = BZDB.get("motto");
956     motto = motto.substr(0, sizeof(startupInfo.motto) - 1);
957     //Flawfinder: ignore
958     strcpy(startupInfo.motto, motto.c_str());
959 
960     // load the default values for shot colors
961     Team::updateShotColors();
962 
963     // make platform factory
964     PlatformFactory* platformFactory = PlatformFactory::getInstance();
965 
966     // open display
967     display = platformFactory->createDisplay(NULL, NULL);
968     if (!display)
969     {
970         printFatalError("Can't open display.  Exiting.");
971         return bail(1);
972     }
973 
974     // choose visual
975     BzfVisual* visual = platformFactory->createVisual(display);
976     setVisual(visual);
977 
978     // make the window
979     BzfWindow* window = platformFactory->createWindow(display, visual);
980     if (!window->isValid())
981     {
982         printFatalError("Can't create window.  Exiting.");
983         return bail(1);
984     }
985     window->setTitle("bzflag");
986 
987     // create the joystick
988     BzfJoystick* joystick = platformFactory->createJoystick();
989 
990     // Change audio driver if requested
991     if (BZDB.isSet("audioDriver"))
992         PlatformFactory::getMedia()->setDriver(BZDB.get("audioDriver"));
993     // Change audio device if requested
994     if (BZDB.isSet("audioDevice"))
995         PlatformFactory::getMedia()->setDevice(BZDB.get("audioDevice"));
996 
997     // set data directory if user specified
998     if (BZDB.isSet("directory"))
999     {
1000         /* already specified, so just set/use it */
1001         PlatformFactory::getMedia()->setMediaDirectory(BZDB.get("directory"));
1002     }
1003     else
1004     {
1005 
1006 #if (defined(_WIN32) || defined(WIN32))
1007         char exePath[MAX_PATH];
1008         char dataPath[MAX_PATH];
1009         GetModuleFileName(NULL,exePath,MAX_PATH);
1010         char* last = strrchr(exePath,'\\');
1011         if (last)
1012             *last = '\0';
1013 
1014         strcpy(dataPath,exePath);
1015 
1016         char temp[MAX_PATH];
1017         getcwd(temp,MAX_PATH);
1018 
1019         // find the data dir
1020         strcat(dataPath,"\\data");
1021 
1022         if (chdir(dataPath) != 0)
1023         {
1024             strcpy(dataPath,exePath);
1025 
1026             char* last = strrchr(dataPath,'\\');
1027             if (last)
1028                 *last = '\0';
1029 
1030             strcat(dataPath,"\\data");
1031         }
1032 
1033         chdir(temp);
1034         BzfMedia *media = PlatformFactory::getMedia();
1035         if (media)
1036             media->setMediaDirectory(dataPath);
1037 
1038         FileManager::instance().setDataPath(std::string(dataPath));
1039 #else
1040         // It's only checking existence of l10n directory
1041         std::string mediadir = DEFAULT_MEDIA_DIR;
1042         mediadir += "/l10n";
1043         DIR *localedir = opendir(mediadir.c_str());
1044         if (localedir != NULL)
1045         {
1046             /* found 'data' dir */
1047             BzfMedia *media = PlatformFactory::getMedia();
1048             if (media)
1049                 media->setMediaDirectory(DEFAULT_MEDIA_DIR);
1050             closedir(localedir);
1051         }
1052         else
1053         {
1054             /* bah, just set the compile-time path */
1055             PlatformFactory::getMedia()->setMediaDirectory(INSTALL_DATA_DIR);
1056         }
1057 #endif
1058     }
1059 
1060     // initialize font system
1061     FontManager &fm = FontManager::instance();
1062     // load fonts from data directory
1063     fm.loadAll(PlatformFactory::getMedia()->getMediaDirectory() + "/fonts");
1064     // try to get a font - only returns -1 if there are no fonts at all
1065     if (fm.getFaceID(BZDB.get("consoleFont")) < 0)
1066     {
1067         printFatalError("No fonts found  (the -directory option may help).  Exiting");
1068         return bail(1);
1069     }
1070 
1071     // initialize locale system
1072 
1073     BundleMgr *bm = new BundleMgr(PlatformFactory::getMedia()->getMediaDirectory(), "bzflag");
1074     World::setBundleMgr(bm);
1075 
1076     std::string locale = BZDB.isSet("locale") ? BZDB.get("locale") : "default";
1077     World::setLocale(locale);
1078     bm->getBundle(World::getLocale());
1079 
1080     bool setPosition = false, setSize = false;
1081     int x = 0, y = 0, w = 0, h = 0;
1082 
1083     // set window size (we do it here because the OpenGL context isn't yet bound)
1084 
1085     if (BZDB.isSet("geometry"))
1086     {
1087         char xs, ys;
1088         const std::string geometry = BZDB.get("geometry");
1089         const int count = sscanf(geometry.c_str(), "%dx%d%c%d%c%d", &w, &h, &xs, &x, &ys, &y);
1090 
1091         if (geometry == "default" || (count != 6 && count != 2) || w < 0 || h < 0)
1092             BZDB.unset("geometry");
1093         else if (count == 6 && ((xs != '-' && xs != '+') || (ys != '-' && ys != '+')))
1094             BZDB.unset("geometry");
1095         else
1096         {
1097             setSize = true;
1098             if (w < 256)
1099                 w = 256;
1100             if (h < 192)
1101                 h = 192;
1102             if (count == 6)
1103             {
1104                 if (xs == '-')
1105                     x = display->getWidth() - x - w;
1106                 if (ys == '-')
1107                     y = display->getHeight() - y - h;
1108                 setPosition = true;
1109             }
1110             // must call this before setFullscreen() is called
1111             display->setPassthroughSize(w, h);
1112         }
1113     }
1114 
1115     // set window size (we do it here because the OpenGL context isn't yet
1116     // bound.)
1117     const bool useFullscreen = needsFullscreen();
1118     if (useFullscreen)
1119     {
1120 #ifndef HAVE_SDL
1121         // tell window to be fullscreen
1122         window->setFullscreen(true);
1123 #endif
1124 
1125         // set the size if one was requested.  this overrides the default
1126         // size (which is the display or passthrough size).
1127         if (setSize)
1128             window->setSize(w, h);
1129     }
1130     else if (setSize)
1131         window->setSize(w, h);
1132     else
1133         window->setSize(640, 480);
1134     if (setPosition)
1135         window->setPosition(x, y);
1136 
1137     // now make the main window wrapper.  this'll cause the OpenGL context
1138     // to be bound for the first time.
1139     MainWindow* pmainWindow = new MainWindow(window, joystick);
1140     if (pmainWindow->isInFault())
1141     {
1142         printFatalError("Error creating window - Exiting");
1143         return bail(1);
1144     }
1145 
1146     std::string videoFormat;
1147     int format = -1;
1148     if (BZDB.isSet("resolution"))
1149     {
1150         videoFormat = BZDB.get("resolution");
1151         if (videoFormat.length() != 0)
1152         {
1153             format = display->findResolution(videoFormat.c_str());
1154             if (format >= 0)
1155                 display->setFullScreenFormat(format);
1156         }
1157     };
1158     // set fullscreen again so MainWindow object knows it's full screen
1159     if (useFullscreen)
1160         // this will also call window create
1161         pmainWindow->setFullscreen();
1162     else
1163         window->create();
1164 
1165     // set main window's minimum size (arbitrary but should be big enough
1166     // to see stuff in control panel)
1167     pmainWindow->setMinSize(256, 192);
1168 
1169     // enable vsync if needed
1170     pmainWindow->getWindow()->setVerticalSync(BZDB.evalInt("saveEnergy") == 2);
1171 
1172     // get sound files.  must do this after creating the window because
1173     // DirectSound is a bonehead API.
1174     if (!noAudio)
1175     {
1176         openSound("bzflag");
1177         if (startupInfo.hasConfiguration && BZDB.isSet("volume"))
1178             setSoundVolume(static_cast<int>(BZDB.eval("volume")));
1179     }
1180 
1181     // Initialize the joystick
1182     joystick->initJoystick(BZDB.get("joystickname").c_str());
1183     joystick->setXAxis(BZDB.get("jsXAxis"));
1184     joystick->setYAxis(BZDB.get("jsYAxis"));
1185 
1186     // initialize graphics state
1187     pmainWindow->getWindow()->makeCurrent();
1188     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1189     glClearStencil(0);
1190     glMatrixMode(GL_PROJECTION);
1191     glLoadIdentity();
1192     glMatrixMode(GL_MODELVIEW);
1193     glLoadIdentity();
1194     glEnable(GL_SCISSOR_TEST);
1195 //  glEnable(GL_CULL_FACE);
1196     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
1197     if (!OpenGLGState::haveGLContext())
1198     {
1199         // DIE
1200         printFatalError("ERROR: Unable to initialize an OpenGL context");
1201         if (display != NULL)
1202         {
1203             delete display;
1204             display=NULL;
1205         }
1206         bail(1);
1207         exit(1);
1208     }
1209     OpenGLGState::init();
1210 
1211     // sanity check - make sure OpenGL was actually initialized or
1212     // there's no sense in continuing.
1213     const char* const glRenderer = (const char*)glGetString(GL_RENDERER);
1214     if (!glRenderer)
1215     {
1216         // bad code, no doughnut for you
1217 
1218         GLenum error = GL_NO_ERROR;
1219         while ((error = glGetError()) != GL_NO_ERROR)
1220         {
1221             switch (error)
1222             {
1223             case GL_INVALID_ENUM:
1224                 std::cerr << "ERROR: GL_INVALID_ENUM" << std::endl;
1225                 break;
1226             case GL_INVALID_VALUE:
1227                 std::cerr << "ERROR: GL_INVALID_VALUE" << std::endl;
1228                 break;
1229             case GL_INVALID_OPERATION:
1230                 std::cerr << "ERROR: GL_INVALID_OPERATION" << std::endl;
1231                 break;
1232             case GL_STACK_OVERFLOW:
1233                 std::cerr << "ERROR: GL_STACK_OVERFLOW" << std::endl;
1234                 break;
1235             case GL_STACK_UNDERFLOW:
1236                 std::cerr << "ERROR: GL_STACK_UNDERFLOW" << std::endl;
1237                 break;
1238             case GL_OUT_OF_MEMORY:
1239                 std::cerr << "ERROR: GL_OUT_OF_MEMORY" << std::endl;
1240                 break;
1241 #ifdef GL_VERSION_1_2
1242             case GL_TABLE_TOO_LARGE:
1243                 std::cerr << "ERROR: GL_TABLE_TOO_LARGE" << std::endl;
1244                 break;
1245 #endif
1246             case GL_NO_ERROR:
1247                 // should not reach
1248                 std::cerr << "ERROR: GL_NO_ERROR" << std::endl;
1249                 break;
1250             default:
1251                 // should not reach
1252                 std::cerr << "ERROR: UNKNOWN CODE: " << error << std::endl;
1253             }
1254         }
1255 
1256         // DIE
1257         printFatalError("ERROR: Unable to initialize an OpenGL renderer");
1258         if (display != NULL)
1259         {
1260             delete display;
1261             display=NULL;
1262         }
1263         bail(1);
1264         exit(1);
1265 
1266     }
1267 
1268     // add the zbuffer callback here, after the OpenGL context is initialized
1269     BZDB.addCallback("zbuffer", setDepthBuffer, NULL);
1270 
1271     //add a fake cursor. Let the defaults file override this, though.
1272     if (!BZDB.isSet("fakecursor"))
1273     {
1274         // check that the glrenderer is Mesa Glide
1275         if ((glRenderer != NULL) && (strncmp(glRenderer, "Mesa Glide", 10) == 0))
1276             BZDB.set("fakecursor", "1");
1277     }
1278 
1279     // set gamma if set in resources and we have gamma control
1280     if (BZDB.isSet("gamma"))
1281     {
1282         if (pmainWindow->getWindow()->hasGammaControl())
1283             pmainWindow->getWindow()->setGamma
1284             ((float)atof(BZDB.get("gamma").c_str()));
1285     }
1286 
1287     // set the scene renderer's window
1288     RENDERER.setWindow(pmainWindow);
1289 
1290     // restore rendering configuration
1291     if (startupInfo.hasConfiguration)
1292     {
1293         if (BZDB.isSet("zbuffersplit"))
1294             RENDERER.setZBufferSplit(BZDB.isTrue("zbuffersplit"));
1295         if (BZDB.isSet("quality"))
1296         {
1297             std::string value = BZDB.get("quality");
1298             const int qualityLevels = (int)configQualityValues.size();
1299             for (int j = 0; j < qualityLevels; j++)
1300             {
1301                 if (value == configQualityValues[j])
1302                 {
1303                     RENDERER.setQuality(j);
1304                     break;
1305                 }
1306             }
1307         }
1308 
1309         TextureManager& tm = TextureManager::instance();
1310         tm.setMaxFilter(BZDB.get("texture"));
1311         BZDB.set("texture", tm.getMaxFilterName());
1312 
1313         BZDB.set("texturereplace", (!BZDBCache::lighting &&
1314                                     RENDERER.useQuality() < 2) ? "1" : "0");
1315         BZDB.setPersistent("texturereplace", false);
1316         if (BZDB.isSet("view"))
1317         {
1318             RENDERER.setViewType(SceneRenderer::Normal);
1319             std::string value = BZDB.get("view");
1320             for (i = 0; i < configViewValues.size(); i++)
1321                 if (value == configViewValues[i])
1322                 {
1323                     RENDERER.setViewType((SceneRenderer::ViewType)i);
1324                     break;
1325                 }
1326         }
1327 
1328         if (BZDB.isSet("startcode"))
1329             ServerStartMenu::setSettings(BZDB.get("startcode").c_str());
1330 
1331         if (BZDB.isSet("panelopacity"))
1332             RENDERER.setPanelOpacity(BZDB.eval("panelopacity"));
1333 
1334         if (BZDB.isSet("radaropacity"))
1335             RENDERER.setRadarOpacity(BZDB.eval("radaropacity"));
1336 
1337         if (BZDB.isSet("radarsize"))
1338             RENDERER.setRadarSize(BZDB.getIntClamped("radarsize", 0, GUIOptionsMenu::maxRadarSize));
1339 
1340         if (BZDB.isSet("panelheight"))
1341             RENDERER.setPanelHeight(BZDB.getIntClamped("panelheight", 0, GUIOptionsMenu::maxRadarSize));
1342 
1343         if (BZDB.isSet("mouseboxsize"))
1344             RENDERER.setMaxMotionFactor(atoi(BZDB.get("mouseboxsize").c_str()));
1345     }
1346 
1347     // grab the mouse only if allowed
1348     if (BZDB.isSet("mousegrab") && !BZDB.isTrue("mousegrab"))
1349     {
1350         pmainWindow->setNoMouseGrab();
1351         pmainWindow->enableGrabMouse(false);
1352     }
1353     else
1354         pmainWindow->enableGrabMouse(true);
1355 
1356     // set window quadrant
1357     if (RENDERER.getViewType() == SceneRenderer::ThreeChannel)
1358         pmainWindow->setQuadrant(MainWindow::UpperRight);
1359     else if (RENDERER.getViewType() == SceneRenderer::Stacked)
1360         pmainWindow->setQuadrant(MainWindow::LowerHalf);
1361 #ifndef USE_GL_STEREO
1362     else if (RENDERER.getViewType() == SceneRenderer::Stereo)
1363         pmainWindow->setQuadrant(MainWindow::UpperRight);
1364 #endif
1365 
1366     // clear the grid graphics if they are not accessible
1367 #if !defined(DEBUG_RENDERING)
1368     if (debugLevel <= 0)
1369     {
1370         BZDB.set("showCullingGrid", "0");
1371         BZDB.set("showCollisionGrid", "0");
1372     }
1373 #endif
1374 
1375     // set server list URL
1376     if (BZDB.isSet("list"))
1377         startupInfo.listServerURL = BZDB.get("list");
1378 
1379     // setup silence list
1380     std::vector<std::string>& list = getSilenceList();
1381 
1382     // search for entries silencedPerson0 silencedPerson1 etc..
1383     // to the database. Stores silenceList
1384     // By only allowing up to a certain # of people can prevent
1385     // the vague chance of buffer overrun.
1386     const int bufferLength = 30;
1387     const int maxListSize = 1000000; // do even that many play bzflag?
1388     char buffer [bufferLength];
1389     bool keepGoing = true;
1390 
1391     for (int s = 0; keepGoing && (s < maxListSize); s++)
1392     {
1393         sprintf(buffer,"silencedPerson%d",s); // could do %-10d
1394 
1395         if (BZDB.isSet(buffer))
1396         {
1397             list.push_back(BZDB.get(buffer));
1398             // remove the value from the database so when we save
1399             // it saves the list's new values in order
1400             BZDB.unset(buffer);
1401         }
1402         else
1403             keepGoing = false;
1404     }
1405 
1406     if (BZDB.isSet("serverCacheAge"))
1407         (ServerListCache::get())->setMaxCacheAge(atoi(BZDB.get("serverCacheAge").c_str()));
1408 
1409     // start playing
1410     startPlaying(display, RENDERER);
1411 
1412     // save resources
1413     if (BZDB.isTrue("saveSettings"))
1414     {
1415         dumpResources();
1416         if (alternateConfig == "")
1417             CFGMGR.write(getCurrentConfigFileName());
1418         else
1419             CFGMGR.write(alternateConfig);
1420     }
1421 
1422     // shut down
1423 
1424 
1425     killGlobalAres();
1426     AresHandler::globalShutdown();
1427 
1428     if (filter != NULL)
1429         delete filter;
1430     filter = NULL;
1431     display->setDefaultResolution();
1432     delete pmainWindow;
1433     delete joystick;
1434     delete window;
1435     delete visual;
1436     closeSound();
1437     delete display;
1438     delete platformFactory;
1439     delete bm;
1440     Flags::kill();
1441 
1442 #ifdef _WIN32
1443     // clean up
1444     WSACleanup();
1445 #endif
1446 
1447     // clean up singletons
1448     //  delete FILEMGR;
1449     //  delete CMDMGR;
1450     //  delete BZDB;
1451 
1452     return 0;
1453 }
1454 //
1455 #if defined(_WIN32) && !defined(HAVE_SDL)
1456 
1457 //
1458 // WinMain()
1459 //  windows entry point.  forward to main()
1460 //
1461 
WinMain(HINSTANCE instance,HINSTANCE,LPSTR _cmdLine,int)1462 int WINAPI      WinMain(HINSTANCE instance, HINSTANCE, LPSTR _cmdLine, int)
1463 {
1464     // convert command line to argc and argv.  note that it's too late
1465     // to do this right because spaces that were embedded in a single
1466     // argument now look like like normal spaces.  not much we can do
1467     // about that.
1468     // FIXME -- argc and argv can be accessible;  use them instead of this.
1469     char* cmdLine = strdup(_cmdLine);
1470 
1471     // count number of arguments
1472     int argc = 1;
1473     char* scan = cmdLine;
1474     while (isspace(*scan) && *scan != 0) scan++;
1475     while (*scan)
1476     {
1477         argc++;
1478         while (!isspace(*scan) && *scan != 0) scan++;
1479         while (isspace(*scan) && *scan != 0) scan++;
1480     }
1481 
1482     // get path to application.  this is ridiculously simple.
1483     char appName[MAX_PATH];
1484     GetModuleFileName(instance,appName,MAX_PATH);
1485 
1486     // make argument list and assign arguments
1487     char** argv = new char*[argc];
1488     argc = 0;
1489     argv[argc++] = appName;
1490     scan = cmdLine;
1491     while (isspace(*scan) && *scan != 0) scan++;
1492     while (*scan)
1493     {
1494         argv[argc++] = scan;
1495         while (!isspace(*scan) && *scan != 0) scan++;
1496         if (*scan) *scan++ = 0;
1497         while (isspace(*scan) && *scan != 0) scan++;
1498     }
1499 
1500     const int exitCode = myMain(argc, argv);
1501 
1502     // clean up and return exit code
1503     delete[] argv;
1504     free(cmdLine);
1505     return exitCode;
1506 }
1507 
1508 #endif /* defined(_WIN32) */
1509 
1510 
1511 // Local Variables: ***
1512 // mode: C++ ***
1513 // tab-width: 4 ***
1514 // c-basic-offset: 4 ***
1515 // indent-tabs-mode: nil ***
1516 // End: ***
1517 // ex: shiftwidth=4 tabstop=4
1518