1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "config.h"
29 #include <fstream>
30 #include <iomanip>
31 #include <iostream>
32 #include "tConfiguration.h"
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <string>
36 #include <sstream>
37 #include "tString.h"
38 #include "tToDo.h"
39 #include "tConsole.h"
40 #include "tDirectories.h"
41 #include "tLocale.h"
42 #include "tRecorder.h"
43 #include "tCommandLine.h"
44 #include "tResourceManager.h"
45 #include "tError.h"
46 
47 #include <vector>
48 #include <string.h>
49 
50 #ifndef WIN32
51 #include <signal.h>
52 #endif
53 
54 /***********************************************************************
55  * The new Configuration interface, currently not completely implemented
56  */
57 
58 #ifndef NEW_CONFIGURATION_NO_COMPILE
59 
60 // Use this function to register your own configuration directive
61 //   returns true if it was successful, false if not
62 //   should return true even if it didn't register the new directive because
63 //       it was already there
registerDirective(string newDirective,string defValue)64 bool tConfiguration::registerDirective(string newDirective, string defValue) {
65     // THIS IS A STUB
66 }
67 
68 // Use this function to set a configuration directive from a string
setDirective(string oldDirective,string newValue)69 bool tConfiguration::setDirective(string oldDirective, string newValue) {
70     // THIS IS A STUB
71 }
72 
73 // Use this function to set a configuration directive from an int
setDirective(string oldDirective,int newValue)74 bool tConfiguration::setDirective(string oldDirective, int newValue) {
75     // THIS IS A STUB
76 }
77 
78 // Use this function to set a configuration directive from a float
setDirective(string oldDirective,double newValue)79 bool tConfiguration::setDirective(string oldDirective, double newValue) {
80     // THIS IS A STUB
81 }
82 
83 // Use this function to set a configuration directive from a bool
setDirective(string oldDirective,bool newValue)84 bool tConfiguration::setDirective(string oldDirective, bool newValue) {
85     // THIS IS A STUB
86 }
87 
88 // Use this function to get a configuration directive as a string
getDirective(string oldDirective)89 const string& tConfiguration::getDirective(string oldDirective) {
90     // THIS IS A STUB
91 }
92 
93 // Use this function to get a configuration directive as an int
getDirectiveI(string oldDirective)94 const int& tConfiguration::getDirectiveI(string oldDirective) {
95     // THIS IS A STUB
96 }
97 
98 // Use this function to get a configuration directive as a double
getDirectiveF(string oldDirective)99 const double& tConfiguration::getDirectiveF(string oldDirective) {
100     // THIS IS A STUB
101 }
102 
103 // Use this function to get a configuration directive as a bool
getDirectiveB(string oldDirective)104 const bool& tConfiguration::getDirectiveB(string oldDirective) {
105     // THIS IS A STUB
106 }
107 
108 // Use this function to load a file
109 //  You *MUST* pass it a complete path from the root directory!
110 //  Returns TRUE if it can open the file and load at least some of it
111 //  Returns FALSE if complete failure.
LoadFile(string filename)112 bool tConfiguration::LoadFile(string filename) {
113     // THIS IS A STUB
114 }
115 
116 // Use this function to save current configuration to files
117 //   Returns TRUE on success, FALSE on failure
SaveFile()118 bool tConfiguration::SaveFile() {
119     // THIS IS A STUB
120 }
121 
122 // This function is used internally to actually set each directive
_setDirective(string oldDirective,tConfigurationItem & newItem)123 bool tConfiguration::_setDirective(string oldDirective, tConfigurationItem& newItem) {
124     // THIS IS A STUB
125 }
126 
127 // This function registers basic configuration directives
128 //  Create a new configuration directive by going to this function and
129 //  putting the appropriate line, then just use it in the game
130 //  It registers global defaults, but not object-specific defaults
_registerDefaults()131 void tConfiguration::_registerDefaults() {
132     // THIS IS A STUB
133 }
134 
135 tConfiguration* tConfiguration::_instance = 0;
136 
GetConfiguration()137 const tConfiguration* tConfiguration::GetConfiguration() {
138     if(_instance = 0) {
139         _instance = new tConfiguration;
140     }
141 
142     return _instance;
143 }
144 
145 #endif
146 /*
147  * The old stuff follows
148  ************************************************************************/
149 
150 bool           tConfItemBase::printChange=true;
151 bool           tConfItemBase::printErrors=true;
152 
153 
154 //! @param newLevel         the new access level to set over the course of the lifetime of this object
155 //! @param allowElevation   only if set to true, getting higher access rights is possible. Use with extreme care.
tCurrentAccessLevel(tAccessLevel newLevel,bool allowElevation)156 tCurrentAccessLevel::tCurrentAccessLevel( tAccessLevel newLevel, bool allowElevation )
157 {
158     // prevent elevation
159     if ( !allowElevation && newLevel < currentLevel_ )
160     {
161         // you probably want to know when this happens in the debugger
162         st_Breakpoint();
163         newLevel = currentLevel_;
164     }
165 
166     lastLevel_ = currentLevel_;
167     currentLevel_ = newLevel;
168 }
169 
170 
tCurrentAccessLevel()171 tCurrentAccessLevel::tCurrentAccessLevel()
172 {
173     lastLevel_ = currentLevel_;
174 }
175 
~tCurrentAccessLevel()176 tCurrentAccessLevel::~tCurrentAccessLevel()
177 {
178     currentLevel_ = lastLevel_;
179 }
180 
181 //! returns the current access level
GetAccessLevel()182 tAccessLevel tCurrentAccessLevel::GetAccessLevel()
183 {
184     tASSERT( currentLevel_ != tAccessLevel_Invalid );
185     return currentLevel_;
186 }
187 
188 // returns the name of an access level
GetName(tAccessLevel level)189 tString tCurrentAccessLevel::GetName( tAccessLevel level )
190 {
191     std::ostringstream s;
192     s << "$config_accesslevel_" << level;
193     return tString( tOutput( s.str().c_str() ) );
194 }
195 
196 tAccessLevel tCurrentAccessLevel::currentLevel_ = tAccessLevel_Invalid; //!< the current access level
197 
tAccessLevelSetter(tConfItemBase & item,tAccessLevel level)198 tAccessLevelSetter::tAccessLevelSetter( tConfItemBase & item, tAccessLevel level )
199 {
200 #ifdef KRAWALL_SERVER
201     item.requiredLevel = level;
202 #endif
203 }
204 
205 static std::map< tString, tConfItemBase * > * st_confMap = 0;
ConfItemMap()206 tConfItemBase::tConfItemMap & tConfItemBase::ConfItemMap()
207 {
208     if (!st_confMap)
209         st_confMap = tNEW( tConfItemMap );
210     return *st_confMap;
211 }
212 
213 static bool st_preventCasacl = false;
214 
tCasaclPreventer(bool prevent)215 tCasaclPreventer::tCasaclPreventer( bool prevent )
216 {
217     previous_ = st_preventCasacl;
218     st_preventCasacl = prevent;
219 }
220 
~tCasaclPreventer()221 tCasaclPreventer::~tCasaclPreventer()
222 {
223     st_preventCasacl = previous_;
224 }
225 
226 // changes the access level of a configuration item
227 class tConfItemLevel: public tConfItemBase
228 {
229 public:
tConfItemLevel()230     tConfItemLevel()
231     : tConfItemBase( "ACCESS_LEVEL" )
232     {
233         requiredLevel = tAccessLevel_Owner;
234     }
235 
ReadVal(std::istream & s)236     virtual void ReadVal(std::istream &s)
237     {
238         // read name and access level
239         tString name;
240         s >> name;
241 
242         int levelInt;
243         s >> levelInt;
244         tAccessLevel level = static_cast< tAccessLevel >( levelInt );
245 
246         if ( s.fail() )
247         {
248             if(printErrors)
249             {
250                 con << tOutput( "$access_level_usage" );
251             }
252             return;
253         }
254 
255         // make name uppercase:
256         tToUpper( name );
257 
258         // find the item
259         tConfItemMap & confmap = ConfItemMap();
260         tConfItemMap::iterator iter = confmap.find( name );
261         if ( iter != confmap.end() )
262         {
263             // and change the level
264             tConfItemBase * ci = (*iter).second;
265             if ( ci->requiredLevel != level )
266             {
267                 ci->requiredLevel = level;
268                 if(printChange)
269                 {
270                     con << tOutput( "$access_level_change", name, tCurrentAccessLevel::GetName( level ) );
271                 }
272             }
273         }
274         else if(printErrors)
275         {
276             con << tOutput( "$config_command_unknown", name );
277         }
278     }
279 
WriteVal(std::ostream & s)280     virtual void WriteVal(std::ostream &s)
281     {
282         tASSERT(0);
283     }
284 
Writable()285     virtual bool Writable(){
286         return false;
287     }
288 
Save()289     virtual bool Save(){
290         return false;
291     }
292 };
293 
294 static tConfItemLevel st_confLevel;
295 
296 #ifdef KRAWALL_SERVER
297 
298 static char const *st_casacl = "CASACL";
299 
300 //! casacl (Check And Set ACcess Level) command: elevates the access level for the context of the current configuration file
301 class tCasacl: tConfItemBase
302 {
303 public:
tCasacl()304     tCasacl()
305     : tConfItemBase( st_casacl )
306     {
307         requiredLevel = tAccessLevel_Program;
308     }
309 
ReadVal(std::istream & s)310     virtual void ReadVal( std::istream & s )
311     {
312         int required_int = 0, elevated_int = 20;
313 
314         // read required and elevated access levels
315         s >> required_int;
316         s >> elevated_int;
317 
318         tAccessLevel elevated = static_cast< tAccessLevel >( elevated_int );
319         tAccessLevel required = static_cast< tAccessLevel >( required_int );
320 
321         if ( s.fail() )
322         {
323             con << tOutput( "$casacl_usage" );
324             throw tAbortLoading( st_casacl );
325         }
326         else if ( tCurrentAccessLevel::GetAccessLevel() > required )
327         {
328             con << tOutput( "$access_level_error",
329                             "SUDO",
330                             tCurrentAccessLevel::GetName( required ),
331                             tCurrentAccessLevel::GetName( tCurrentAccessLevel::GetAccessLevel() )
332                 );
333             throw tAbortLoading( st_casacl );
334         }
335         else if ( st_preventCasacl )
336         {
337             con << tOutput( "$casacl_not_allowed" );
338             throw tAbortLoading( st_casacl );
339         }
340         else
341         {
342             tString().ReadLine(s); // prevent commands following this one without a newline
343             tCurrentAccessLevel::currentLevel_ = elevated;
344         }
345     }
346 
WriteVal(std::ostream & s)347     virtual void WriteVal(std::ostream &s)
348     {
349         tASSERT(0);
350     }
351 
Writable()352     virtual bool Writable(){
353         return false;
354     }
355 
Save()356     virtual bool Save(){
357         return false;
358     }
359 };
360 
361 static tCasacl st_sudo;
362 
363 #endif
364 
365 bool st_FirstUse=true;
366 static tConfItem<bool> fu("FIRST_USE",st_FirstUse);
367 //static tConfItem<bool> fu("FIRST_USE","help_first_use",st_FirstUse);
368 
369 
tAbortLoading(char const * command)370 tAbortLoading::tAbortLoading( char const * command )
371 : command_( command )
372 {
373 }
374 
DoGetName() const375 tString tAbortLoading::DoGetName() const
376 {
377     return tString(tOutput( "$abort_loading_name"));
378 }
379 
DoGetDescription() const380 tString tAbortLoading::DoGetDescription() const
381 {
382     return tString(tOutput( "$abort_loading_description", command_ ));
383 }
384 
tConfItemBase(const char * t)385 tConfItemBase::tConfItemBase(const char *t)
386         :id(-1),title(t),
387 changed(false){
388 
389     tConfItemMap & confmap = ConfItemMap();
390     if ( confmap.find( title ) != confmap.end() )
391         tERR_ERROR_INT("Two tConfItems with the same name " << t << "!");
392 
393     // compose help name
394     tString helpname;
395     helpname << title << "_help";
396     tToLower( helpname );
397 
398     const_cast<tOutput&>(help).AddLocale(helpname);
399 
400     confmap[title] = this;
401 
402     requiredLevel = tAccessLevel_Admin;
403     setLevel      = tAccessLevel_Owner;
404 }
405 
tConfItemBase(const char * t,const tOutput & h)406 tConfItemBase::tConfItemBase(const char *t, const tOutput& h)
407         :id(-1),title(t), help(h),
408 changed(false){
409 
410     tConfItemMap & confmap = ConfItemMap();
411     if ( confmap.find( title ) != confmap.end() )
412         tERR_ERROR_INT("Two tConfItems with the same name " << t << "!");
413 
414     confmap[title] = this;
415 
416     requiredLevel = tAccessLevel_Admin;
417     setLevel      = tAccessLevel_Owner;
418 }
419 
~tConfItemBase()420 tConfItemBase::~tConfItemBase()
421 {
422     tConfItemMap & confmap = ConfItemMap();
423     confmap.erase(title);
424     if ( confmap.size() == 0 )
425     {
426         delete st_confMap;
427         st_confMap = 0;
428     }
429 }
430 
SaveAll(std::ostream & s)431 void tConfItemBase::SaveAll(std::ostream &s){
432     tConfItemMap & confmap = ConfItemMap();
433     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
434     {
435         tConfItemBase * ci = (*iter).second;
436         if (ci->Save()){
437             s << std::setw(28) << ci->title << " ";
438             ci->WriteVal(s);
439             s << '\n';
440         }
441     }
442 }
443 
EatWhitespace(std::istream & s)444 int tConfItemBase::EatWhitespace(std::istream &s){
445     int c=' ';
446 
447     while(isblank(c) &&
448             c!='\n'    &&
449             s.good()  &&
450             !s.eof())
451         c=s.get();
452 
453     if( s.good() )
454     {
455         s.putback(c);
456     }
457 
458     return c;
459 }
460 
LoadLine(std::istream & s)461 void tConfItemBase::LoadLine(std::istream &s){
462     if(!s.eof() && s.good()){
463         tString name;
464         s >> name;
465 
466         // make name uppercase:
467         tToUpper( name );
468 
469         bool found=false;
470 
471         if (name[0]=='#'){ // comment. ignore rest of line
472             char c=' ';
473             while(c!='\n' && s.good() && !s.eof()) c=s.get();
474             found=true;
475         }
476 
477         if (strlen(name)==0) // ignore empty lines
478             found=true;
479 
480         tConfItemMap & confmap = ConfItemMap();
481         tConfItemMap::iterator iter = confmap.find( name );
482         if ( iter != confmap.end() )
483         {
484             tConfItemBase * ci = (*iter).second;
485 
486             bool cb=ci->changed;
487             ci->changed=false;
488 
489             if ( ci->requiredLevel >= tCurrentAccessLevel::GetAccessLevel() )
490             {
491                 ci->setLevel = tCurrentAccessLevel::GetAccessLevel();
492 
493                 ci->ReadVal(s);
494                 if (ci->changed)
495                 {
496                     ci->WasChanged();
497                 }
498                 else
499                 {
500                     ci->changed=cb;
501                 }
502             }
503             else
504             {
505                 tString discard;
506                 discard.ReadLine(s);
507 
508                 con << tOutput( "$access_level_error",
509                                 name,
510                                 tCurrentAccessLevel::GetName( ci->requiredLevel ),
511                                 tCurrentAccessLevel::GetName( tCurrentAccessLevel::GetAccessLevel() )
512                     );
513                 return;
514             }
515 
516             found=true;
517         }
518 
519         if (!found){
520             // eat rest of input line
521             tString rest;
522             rest.ReadLine( s );
523 
524             if (printErrors)
525             {
526                 tOutput o;
527                 o.SetTemplateParameter(1, name);
528                 o << "$config_command_unknown";
529                 con << o;
530 
531                 if (printChange)
532                 {
533                     int sim_maxlen=-1;
534 
535                     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
536                     {
537                         tConfItemBase * ci = (*iter).second;
538                         if (strstr(ci->title,name) &&
539                                 static_cast<int>(strlen(ci->title)) > sim_maxlen)
540                             sim_maxlen=strlen(ci->title);
541                     }
542 
543                     if (sim_maxlen>0 && printChange ){
544                         int len = name.Len()-1;
545                         int printMax = 1 + 3 * len * len * len;
546                         con << tOutput("$config_command_other");
547                         for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
548                         {
549                             tConfItemBase * ci = (*iter).second;
550                             if (strstr(ci->title,name))
551                             {
552                                 tString help ( ci->help );
553                                 if ( --printMax > 0 )
554                                 {
555                                     tString mess;
556                                     mess << ci->title;
557                                     mess.SetPos( sim_maxlen+2, false );
558                                     mess << "(";
559                                     mess << help;
560                                     mess << ")\n";
561                                     con << mess;
562                                 }
563                             }
564                         }
565                         if (printMax <= 0 )
566                             con << tOutput("$config_command_more");
567                     }
568                 }
569                 else
570                 {
571                     con << '\n';
572                 }
573             }
574         }
575     }
576 
577     //  std::cout << line << " lines read.\n";
578 }
579 
580 static char const * recordingSection = "CONFIG";
581 
582 /*
583 bool LoadAllHelper(std::istream * s)
584 {
585     tString line;
586 
587     // read line from recording
588     if ( s || !tRecorder::Playback( recordingSection, line ) )
589     {
590         // return on failure
591         if (!s)
592             return false;
593 
594         // read line from stream
595         line.ReadLine( *s );
596 
597         // write line to recording
598         tRecorder::Record( recordingSection, line );
599     }
600     line << '\n';
601 
602     // process line
603     std::stringstream str(static_cast< char const * >( line ) );
604     tConfItemBase::LoadLine(str);
605 
606     return true;
607 }
608 */
609 
610 // test if a line should enter the recording; check if the line contains one of the passed
611 // substrings. The array of substrings is supposed to be zero terminated.
s_Veto(tString line_in,std::vector<tString> const & vetos)612 static bool s_Veto( tString line_in, std::vector< tString > const & vetos )
613 {
614     // make name uppercase:
615     tToUpper( line_in );
616 
617     // eat whitespace at the beginning
618     char const * test = line_in;
619     while( isblank(*test) )
620         test++;
621 
622     // skip "LAST_"
623     tString line( test );
624     if ( line.StartsWith( "LAST_" ) )
625         line = tString( static_cast< char const * >(line) + 5 );
626 
627     // iterate throug vetoed config items and test each one
628     for ( std::vector< tString >::const_iterator iter = vetos.begin(); iter != vetos.end(); ++iter )
629     {
630         tString const & veto = *iter;
631 
632         if ( line.StartsWith( veto ) )
633         {
634 #ifdef DEBUG_X
635             if ( !line.StartsWith( "INCLUDE" ) && tRecorder::IsRunning() )
636             {
637                 con << "Veto on config line: " << line << "\n";
638             }
639 #endif
640 
641             return true;
642         }
643     }
644 
645     return false;
646 }
647 
648 // test if a line should be read from a recording
649 // sound and video mode settings shold not
st_Stringify(char const * vetos[])650 static std::vector< tString > st_Stringify( char const * vetos[] )
651 {
652     std::vector< tString > ret;
653 
654     char const * * v = vetos;
655     while ( *v )
656     {
657         ret.push_back( tString( *v ) );
658         ++v;
659     }
660 
661     return ret;
662 }
663 
s_VetoPlayback(tString const & line)664 static bool s_VetoPlayback( tString const & line )
665 {
666     static char const * vetos_char[]=
667         { "USE_DISPLAYLISTS", "CHECK_ERRORS", "ZDEPTH",
668           "COLORDEPTH", "FULLSCREEN ", "ARMAGETRON_LAST_WINDOWSIZE",
669           "ARMAGETRON_WINDOWSIZE", "ARMAGETRON_LAST_SCREENMODE",
670           "ARMAGETRON_SCREENMODE", "CUSTOM_SCREEN", "SOUND",
671           "PASSWORD", "ADMIN_PASS",
672           "ZTRICK", "MOUSE_GRAB", "PNG_SCREENSHOT", // "WHITE_SPARKS", "SPARKS",
673           "KEEP_WINDOW_ACTIVE", "TEXTURE_MODE", "TEXTURES_HI", "LAG_O_METER", "INFINITY_PLANE",
674           "SKY_WOBBLE", "LOWER_SKY", "UPPER_SKY", "DITHER", "HIGH_RIM", "FLOOR_DETAIL",
675           "FLOOR_MIRROR", "SHOW_FPS", "TEXT_OUT", "SMOOTH_SHADING", "ALPHA_BLEND",
676           "PERSP_CORRECT", "POLY_ANTIALIAS", "LINE_ANTIALIAS", "FAST_FORWARD_MAXSTEP",
677           "DEBUG_GNUPLOT", "FLOOR_", "MOVIEPACK_", "RIM_WALL_",
678           0 };
679 
680     static std::vector< tString > vetos = st_Stringify( vetos_char );
681 
682     // delegate
683     return s_Veto( line, vetos );
684 }
685 
686 // test if a line should enter the recording
687 // passwords should not.
s_VetoRecording(tString const & line)688 static bool s_VetoRecording( tString const & line )
689 {
690     static char const * vetos_char[]=
691         { "#", "PASSWORD", "ADMIN_PASS", "LOCAL_USER", "LOCAL_TEAM",
692           0 };
693 
694     static std::vector< tString > vetos = st_Stringify( vetos_char );
695 
696     // delegate
697     return s_Veto( line, vetos ) || s_VetoPlayback( line );
698 }
699 
700 
701 //! @param s        stream to read from
702 //! @param record   set to true if the configuration is to be recorded and played back. That's usually only required if s is a file stream.
LoadAll(std::istream & s,bool record)703 void tConfItemBase::LoadAll(std::istream &s, bool record ){
704     tCurrentAccessLevel levelResetter;
705 
706     try{
707 
708     while(!s.eof() && s.good())
709     {
710         tString line;
711 
712         // read line from stream
713         line.ReadLine( s );
714 
715         /// concatenate lines ending in a backslash
716         while ( line.Len() > 1 && line[line.Len()-2] == '\\' && s.good() && !s.eof() )
717         {
718             line[line.Len()-2] = '\0';
719 
720             // unless it is a double backslash
721             if ( line.Len() > 2 && line[line.Len()-3] == '\\' )
722             {
723                 break;
724             }
725 
726             line.SetLen( line.Len()-1 );
727             tString rest;
728             rest.ReadLine( s );
729             line << rest;
730         }
731 
732         if ( line.Len() <= 1 )
733             continue;
734 
735         // write line to recording
736         if ( record && !s_VetoRecording( line ) )
737         {
738             // don't record supid admins' instant chat logins
739             static tString instantChat("INSTANT_CHAT_STRING");
740             if ( line.StartsWith( instantChat ) && strstr( line, "/login" ) )
741             {
742                 tString newLine = line.SubStr( 0, strstr( line, "/login" ) - (char const *)line );
743                 newLine += "/login NONE";
744                 if ( line[strlen(line)-1] == '\\' )
745                     newLine += '\\';
746                 tRecorder::Record( recordingSection, newLine );
747             }
748             else
749                 tRecorder::Record( recordingSection, line );
750         }
751 
752         //        std::cout << line << '\n';
753 
754         // process line
755         // line << '\n';
756         if ( !record || !tRecorder::IsPlayingBack() || s_VetoPlayback( line ) )
757         {
758             std::stringstream str(static_cast< char const * >( line ) );
759             tConfItemBase::LoadLine(str);
760             // std::cout << line << '\n';
761         }
762     }
763     }
764     catch( tAbortLoading const & e )
765     {
766         // loading was aborted
767         con << e.GetDescription() << "\n";
768     }
769 }
770 
DocAll(std::ostream & s)771 void tConfItemBase::DocAll(std::ostream &s){
772     tConfItemMap & confmap = ConfItemMap();
773     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
774     {
775         tConfItemBase * ci = (*iter).second;
776 
777         tString help ( ci->help );
778         if ( help != "UNDOCUMENTED" )
779         {
780             tString line;
781             line << ci->title;
782             line.SetPos( 30, false );
783             line << help;
784             s << line << '\n';
785         }
786     }
787 }
788 
789 //! @param s        stream to read from
790 //! @param record   set to true if the configuration is to be recorded and played back. That's usually only required if s is a file stream, so it defaults to true here.
LoadAll(std::ifstream & s,bool record)791 void tConfItemBase::LoadAll(std::ifstream &s, bool record )
792 {
793     std::istream &ss(s);
794     LoadAll( ss, record );
795 }
796 
797 
798 //! @param s        file stream to be used for reading later
799 //! @param filename name of the file to open
800 //! @param path     whether to look in var directory
801 //! @return success flag
OpenFile(std::ifstream & s,tString const & filename,SearchPath path)802 bool tConfItemBase::OpenFile( std::ifstream & s, tString const & filename, SearchPath path )
803 {
804     bool ret = ( ( path & Config ) && tDirectories::Config().Open(s, filename ) ) || ( ( path & Var ) && st_StringEndsWith(filename, ".cfg") && tDirectories::Var().Open(s, filename ) );
805 
806     static char const * section = "INCLUDE_VOTE";
807     tRecorder::Playback( section, ret );
808     tRecorder::Record( section, ret );
809 
810     return ret;
811 }
812 
813 //! @param s        file to read from
ReadFile(std::ifstream & s)814 void tConfItemBase::ReadFile( std::ifstream & s )
815 {
816     if ( !tRecorder::IsPlayingBack() )
817     {
818         tConfItemBase::LoadAll(s, true );
819     }
820     else
821     {
822         tConfItemBase::LoadPlayback();
823     }
824 }
825 
826 /*
827   void tConfItemBase::ReadVal(std::istream &s);
828   void tConfItemBase::WriteVal(std::istream &s);
829 */
830 
831 /*
832 tString configfile(){
833 	tString f;
834 	//#ifndef WIN32
835 	//  f << static_cast<const char *>(getenv("HOME"));
836 	//  f << st_LogDir <<
837 	//  f << "/.ArmageTronrc";
838 	//#else
839 		const tPath& vpath = tDirectories::Var();
840 		for ( int prio = vpath.MaxPriority(); prio>=0; --prio )
841 		{
842 			tString path = vpath.Path( prio );
843 			prio = -1;
844 
845 	f << st_LogDir << "/user.cfg";
846 	//#endif
847 	return f;
848 }
849 */
850 
LoadPlayback(bool print)851 bool tConfItemBase::LoadPlayback( bool print )
852 {
853     if ( !tRecorder::IsPlayingBack() )
854         return false;
855 
856     // read line from recording
857     tString line;
858     while ( tRecorder::Playback( recordingSection, line ) )
859     {
860         tRecorder::Record( recordingSection, line );
861         if ( !s_VetoPlayback( line ) )
862         {
863             // process line
864             if ( print ) con << "Playback input : " << line << '\n';
865             std::stringstream str(static_cast< char const * >( line ) );
866             tConfItemBase::LoadLine(str);
867         }
868     }
869 
870     return true;
871 }
872 
Load(const tPath & path,const char * filename)873 static bool Load( const tPath& path, const char* filename )
874 {
875     // read from file
876     if ( !filename )
877     {
878         return false;
879     }
880 
881     std::ifstream s;
882     if ( path.Open( s, filename ) )
883     {
884         tConfItemBase::LoadAll( s, true );
885         return true;
886     }
887     else
888     {
889         return false;
890     }
891 }
892 
893 // flag indicating whether settings were read from a playback
894 static bool st_settingsFromRecording = false;
895 
896 #ifdef DEDICATED
897 tString extraConfig("NONE");
898 
899 class tExtraConfigCommandLineAnalyzer: public tCommandLineAnalyzer
900 {
901 private:
DoAnalyze(tCommandLineParser & parser)902     virtual bool DoAnalyze( tCommandLineParser & parser )
903     {
904         return parser.GetOption(extraConfig, "--extraconfig", "-e");
905     }
906 
DoHelp(std::ostream & s)907     virtual void DoHelp( std::ostream & s )
908     {                                      //
909         s << "-e, --extraconfig            : open an extra configuration file after\n"
910         << "                               settings_dedicated.cfg\n";
911     }
912 };
913 
914 static tExtraConfigCommandLineAnalyzer s_extraAnalyzer;
915 #endif
916 
917 static void st_InstallSigHupHandler();
918 
st_LoadConfig(bool printChange)919 void st_LoadConfig( bool printChange )
920 {
921     // default include files are executed at owner level
922     tCurrentAccessLevel level( tAccessLevel_Owner, true );
923 
924     st_InstallSigHupHandler();
925 
926     const tPath& var = tDirectories::Var();
927     const tPath& config = tDirectories::Config();
928     const tPath& data = tDirectories::Data();
929 
930     tConfItemBase::printChange=printChange;
931 #ifdef DEDICATED
932     tConfItemBase::printErrors=false;
933 #endif
934     {
935         Load( var, "user.cfg" );
936     }
937     tConfItemBase::printErrors=true;
938 
939     Load( config, "settings.cfg" );
940 #ifdef DEDICATED
941     Load( config, "settings_dedicated.cfg" );
942     if( extraConfig != "NONE" ) Load( config, extraConfig );
943 #else
944     if (st_FirstUse)
945     {
946         Load( config, "default.cfg" );
947     }
948 #endif
949 
950     Load( data, "moviepack/settings.cfg" );
951 
952     Load( config, "autoexec.cfg" );
953     Load( var, "autoexec.cfg" );
954 
955     // load configuration from playback
956     tConfItemBase::LoadPlayback();
957     st_settingsFromRecording = tRecorder::IsPlayingBack();
958 
959     tConfItemBase::printChange=true;
960 }
961 
st_SaveConfig()962 void st_SaveConfig()
963 {
964     // don't save while playing back
965     if ( st_settingsFromRecording )
966     {
967         return;
968     }
969 
970     std::ofstream s;
971     if ( tDirectories::Var().Open( s, "user.cfg", std::ios::out, true ) )
972     {
973         tConfItemBase::SaveAll(s);
974     }
975     else
976     {
977         tOutput o("$config_file_write_error");
978         con << o;
979         std::cerr << o;
980     }
981 }
982 
st_LoadConfig()983 void st_LoadConfig()
984 {
985     st_LoadConfig( false );
986 }
987 
st_DoHandleSigHup()988 static void st_DoHandleSigHup()
989 {
990     con << tOutput("$config_sighup");
991     st_SaveConfig();
992     st_LoadConfig();
993 }
994 
st_HandleSigHup(int signal)995 static void st_HandleSigHup( int signal )
996 {
997     st_ToDo_Signal( st_DoHandleSigHup );
998 }
999 
st_InstallSigHupHandler()1000 static void st_InstallSigHupHandler()
1001 {
1002 #ifndef WIN32
1003     static bool installed = false;
1004     if ( !installed )
1005     {
1006         signal( SIGHUP, &st_HandleSigHup );
1007         installed = true;
1008     }
1009 #endif
1010 }
1011 
ReadVal(std::istream & s)1012 void tConfItemLine::ReadVal(std::istream &s){
1013     tString dummy;
1014     dummy.ReadLine(s, true);
1015     if(strcmp(dummy,*target)){
1016         if (printChange)
1017         {
1018             tColoredString oldval;
1019             oldval << *target << tColoredString::ColorString(1,1,1);
1020             tColoredString newval;
1021             newval << dummy << tColoredString::ColorString(1,1,1);
1022             tOutput o;
1023             o.SetTemplateParameter(1, title);
1024             o.SetTemplateParameter(2, oldval);
1025             o.SetTemplateParameter(3, newval);
1026             o << "$config_value_changed";
1027             con << o;
1028         }
1029         *target=dummy;
1030         changed=true;
1031     }
1032 
1033     *target=dummy;
1034 }
1035 
1036 
WriteVal(std::ostream & s)1037 void tConfItemLine::WriteVal(std::ostream &s){
1038     tConfItem<tString>::WriteVal(s);
1039 
1040     // double trailing backslash so it is read back as a single backslash, not
1041     // a continued line (HACK: this would actually be the job of the calling function)
1042     if ( target->Len() >= 2 &&
1043             target->operator()(target->Len() - 2) == '\\' )
1044         s << "\\";
1045 }
1046 
tConfItemFunc(const char * title,CONF_FUNC * func)1047 tConfItemFunc::tConfItemFunc
1048 (const char *title, CONF_FUNC *func)
1049     :tConfItemBase(title),f(func){}
1050 
~tConfItemFunc()1051 tConfItemFunc::~tConfItemFunc(){}
1052 
ReadVal(std::istream & s)1053 void tConfItemFunc::ReadVal(std::istream &s){(*f)(s);}
WriteVal(std::ostream &)1054 void tConfItemFunc::WriteVal(std::ostream &){}
1055 
Save()1056 bool tConfItemFunc::Save(){return false;}
1057 
Include(std::istream & s,bool error)1058 static void Include(std::istream& s, bool error )
1059 {
1060     // allow CASACL
1061     tCasaclPreventer allower(false);
1062 
1063     tString file;
1064     s >> file;
1065 
1066     // refuse to load illegal paths
1067     if( !tPath::IsValidPath( file ) )
1068         return;
1069 
1070     if ( !tRecorder::IsPlayingBack() )
1071     {
1072         // really load include file
1073         if ( !st_StringEndsWith(file, ".cfg") || !Load( tDirectories::Var(), file ) )
1074         {
1075             if (!Load( tDirectories::Config(), file ) && error )
1076             {
1077                 con << tOutput( "$config_include_not_found", file );
1078             }
1079         }
1080     }
1081     else
1082     {
1083         // just read configuration, and don't forget to reset the config level
1084         tCurrentAccessLevel levelResetter;
1085         tConfItemBase::LoadPlayback();
1086     }
1087 
1088     // mark section
1089     char const * section = "INCLUDE_END";
1090     tRecorder::Record( section );
1091     tRecorder::PlaybackStrict( section );
1092 
1093 }
1094 
Include(std::istream & s)1095 static void Include(std::istream& s )
1096 {
1097     Include( s, true );
1098 }
1099 
SInclude(std::istream & s)1100 static void SInclude(std::istream& s )
1101 {
1102     Include( s, false );
1103 }
1104 
1105 static tConfItemFunc s_Include("INCLUDE",  &Include);
1106 static tConfItemFunc s_SInclude("SINCLUDE",  &SInclude);
1107 
1108 // obsoleted settings that still are around in some distruted configuration files
st_Dummy(std::istream & s)1109 static void st_Dummy(std::istream &s){tString rest; rest.ReadLine(s);}
1110 static tConfItemFunc st_DummyMpHack("MOVIEPACK_HACK",&st_Dummy);
1111 
1112 #ifdef DEDICATED
1113 // settings missing in the dedicated server
1114 static tConfItemFunc st_Dummy1("ARENA_WALL_SHADOW_NEAR", &st_Dummy);
1115 static tConfItemFunc st_Dummy2("ARENA_WALL_SHADOW_DIST", &st_Dummy);
1116 static tConfItemFunc st_Dummy3("ARENA_WALL_SHADOW_SIDEDIST", &st_Dummy);
1117 static tConfItemFunc st_Dummy4("ARENA_WALL_SHADOW_SIZE", &st_Dummy);
1118 static tConfItemFunc st_Dummy5("BUG_TRANSPARENCY_DEMAND", &st_Dummy);
1119 static tConfItemFunc st_Dummy6("BUG_TRANSPARENCY", &st_Dummy);
1120 static tConfItemFunc st_Dummy7("SHOW_OWN_NAME", &st_Dummy);
1121 static tConfItemFunc st_Dummy8("FADEOUT_NAME_DELAY", &st_Dummy);
1122 static tConfItemFunc st_Dummy9("FLOOR_MIRROR_INT", &st_Dummy);
1123 #endif
1124 #ifndef DEBUG
1125 // settings missing in optimized mode
1126 static tConfItemFunc st_Dummy10("SIMULATE_RECEIVE_PACKET_LOSS", &st_Dummy);
1127 static tConfItemFunc st_Dummy11("SIMULATE_SEND_PACKET_LOSS", &st_Dummy);
1128 #endif
1129 
1130