-Started to move stuff from library back to main game
[supertux.git] / src / main.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 // 
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 //  02111-1307, USA.
20
21 #include <config.h>
22
23 #include "main.h"
24
25 #include <stdexcept>
26 #include <iostream>
27 #include <sstream>
28 #include <time.h>
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <unistd.h>
34 #ifndef WIN32
35 #include <libgen.h>
36 #endif
37 #include <SDL.h>
38 #include <SDL_mixer.h>
39 #include <SDL_image.h>
40
41 #include "gameconfig.h"
42 #include "resources.h"
43 #include "app/globals.h"
44 #include "app/setup.h"
45 #include "app/gettext.h"
46 #include "audio/sound_manager.h"
47 #include "control/joystickkeyboardcontroller.h"
48 #include "misc.h"
49 #include "game_session.h"
50
51 SDL_Surface* screen = 0;
52 JoystickKeyboardController* main_controller = 0;
53
54 static void init_config()
55 {
56   config = new Config();
57   try {
58     config->load();
59   } catch(std::exception& e) {
60 #ifdef DEBUG
61     std::cerr << "Couldn't load config file: " << e.what() << "\n";
62 #endif
63   }
64 }
65
66 static void find_directories()
67 {
68   const char* home = getenv("HOME");
69   if(home == 0) {
70 #ifdef DEBUG
71     std::cerr << "Couldn't find home directory.\n";
72 #endif
73     home = ".";
74   }
75
76   user_dir = home;
77   user_dir += "/.supertux";
78
79   // Remove .supertux config file from old versions
80   if(FileSystem::faccessible(user_dir)) {
81     std::cerr << "Removing old config file " << user_dir << "\n";
82     remove(user_dir.c_str());
83   }
84
85   // create directories
86   std::string savedir = user_dir + "/save";
87   mkdir(user_dir.c_str(), 0755);
88   mkdir(savedir.c_str(), 0755);
89
90   // try current directory as datadir
91   if(datadir.empty()) {
92     if(FileSystem::faccessible("./data/credits.txt")) {
93       datadir = "./data/";
94     }
95   }
96
97   // Detect datadir with some linux magic
98 #ifndef WIN32
99   if(datadir.empty()) {
100     char exe_file[PATH_MAX];
101     if(readlink("/proc/self/exe", exe_file, PATH_MAX) < 0) {
102 #ifdef DEBUG
103       std::cerr << "Couldn't read /proc/self/exe \n";
104 #endif
105     } else {
106       std::string exedir = std::string(dirname(exe_file)) + "/";
107       std::string testdir = exedir + "./data/";
108       if(access(testdir.c_str(), F_OK) == 0) {
109         datadir = testdir;
110       }
111       
112       testdir = exedir + "../share/supertux/";
113       if(datadir.empty() && access(testdir.c_str(), F_OK) == 0) {
114         datadir = testdir;
115       }
116     }  
117   }
118 #endif
119   
120 #ifdef DATA_PREFIX
121   // use default location
122   if(datadir.empty()) {
123     datadir = DATA_PREFIX;
124   }
125 #endif
126
127   if(datadir.empty())
128     throw std::runtime_error("Couldn't find datadir");
129 }
130
131 static void init_tinygettext()
132 {
133   dictionary_manager.add_directory(datadir + "/locale");
134   dictionary_manager.set_charset("iso8859-1");
135 }
136
137 static void print_usage(const char* argv0)
138 {
139   fprintf(stderr, _("Usage: %s [OPTIONS] LEVELFILE\n\n"), argv0);
140   fprintf(stderr,
141           _("Options:\n"
142             "  -f, --fullscreen             Run in fullscreen mode.\n"
143             "  -w, --window                 Run in window mode.\n"
144             "  -g, --geometry WIDTHxHEIGHT  Run SuperTux in give resolution\n"
145             "  --help                       Show this help message\n"
146             "  --version                    Display SuperTux version and quit\n"
147             "\n"));
148 }
149
150 static void parse_commandline(int argc, char** argv)
151 {
152   for(int i = 1; i < argc; ++i) {
153     std::string arg = argv[i];
154
155     if(arg == "--fullscreen" || arg == "-f") {
156       config->use_fullscreen = true;
157     } else if(arg == "--window" || arg == "-w") {
158       config->use_fullscreen = false;
159     } else if(arg == "--geometry" || arg == "-g") {
160       if(i+1 >= argc) {
161         print_usage(argv[0]);
162         throw std::runtime_error("Need to specify a parameter for geometry switch");
163       }
164       if(sscanf(argv[++i], "%dx%d", &config->screenwidth, &config->screenheight)
165          != 2) {
166         print_usage(argv[0]);
167         throw std::runtime_error("Invalid geometry spec, should be WIDTHxHEIGHT");
168       }
169     } else if(arg == "--show-fps") {
170       config->show_fps = true;
171     } else if(arg == "--help") {
172       print_usage(argv[0]);
173       throw std::runtime_error("");
174     } else if(arg == "--version") {
175       std::cerr << PACKAGE_NAME << " " << PACKAGE_VERSION << "\n";
176       throw std::runtime_error("");
177     } else if(arg[0] != '-') {
178       config->start_level = arg;
179     } else {
180       std::cerr << "Unknown option '" << arg << "'.\n";
181       std::cerr << "Use --help to see a list of options.\n";
182     }
183   }
184
185   // TODO joystick switchyes...
186 }
187
188 static void init_sdl()
189 {
190   if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
191     std::stringstream msg;
192     msg << "Couldn't initialize SDL: " << SDL_GetError();
193     throw std::runtime_error(msg.str());
194   }
195
196   SDL_EnableUNICODE(1);
197 }
198
199 static void check_gl_error()
200 {
201   GLenum glerror = glGetError();
202   std::string errormsg;
203   
204   if(glerror != GL_NO_ERROR) {
205     switch(glerror) {
206       case GL_INVALID_ENUM:
207         errormsg = "Invalid enumeration value";
208         break;
209       case GL_INVALID_VALUE:
210         errormsg = "Numeric argzment out of range";
211         break;
212       case GL_INVALID_OPERATION:
213         errormsg = "Invalid operation";
214         break;
215       case GL_STACK_OVERFLOW:
216         errormsg = "stack overflow";
217         break;
218       case GL_STACK_UNDERFLOW:
219         errormsg = "stack underflow";
220         break;
221       case GL_OUT_OF_MEMORY:
222         errormsg = "out of memory";
223         break;
224       case GL_TABLE_TOO_LARGE:
225         errormsg = "table too large";
226         break;
227       default:
228         errormsg = "unknown error number";
229         break;
230     }
231     std::stringstream msg;
232     msg << "OpenGL Error: " << errormsg;
233     throw std::runtime_error(msg.str());
234   }
235 }
236
237 void init_video()
238 {
239   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 
240   SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
241   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
242   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
243   
244   int flags = SDL_OPENGL;
245   if(config->use_fullscreen)
246     flags |= SDL_FULLSCREEN;
247   int width = config->screenwidth;
248   int height = config->screenheight;
249   int bpp = 0;
250
251   screen = SDL_SetVideoMode(width, height, bpp, flags);
252   if(screen == 0) {
253     std::stringstream msg;
254     msg << "Couldn't set video mode (" << width << "x" << height
255         << "-" << bpp << "bpp): " << SDL_GetError();
256     throw std::runtime_error(msg.str());
257   }
258
259   SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
260
261   // set icon
262   SDL_Surface* icon = IMG_Load(
263     get_resource_filename("images/supertux.xpm").c_str());
264   if(icon != 0) {
265     SDL_WM_SetIcon(icon, 0);
266     SDL_FreeSurface(icon);
267   }
268 #ifdef DEBUG
269   else {
270     std::cerr << "Warning: Couldn't find icon 'images/supertux.xpm'.\n";
271   }
272 #endif
273
274   // setup opengl state and transform
275   glDisable(GL_DEPTH_TEST);
276   glDisable(GL_CULL_FACE);
277
278   glViewport(0, 0, screen->w, screen->h);
279   glMatrixMode(GL_PROJECTION);
280   glLoadIdentity();
281   // logical resolution here not real monitor resolution
282   glOrtho(0, 800, 600, 0, -1.0, 1.0);
283   glMatrixMode(GL_MODELVIEW);
284   glLoadIdentity();
285   glTranslatef(0, 0, 0);
286
287   check_gl_error();
288
289   Surface::reload_all();
290 }
291
292 static void init_audio()
293 {
294   sound_manager = new SoundManager();
295   
296   int format = MIX_DEFAULT_FORMAT;
297   if(Mix_OpenAudio(config->audio_frequency, format, config->audio_channels,
298                    config->audio_chunksize) < 0) {
299     std::cerr << "Couldn't initialize audio ("
300               << config->audio_frequency << "HZ, " << config->audio_channels
301               << " Channels, Format " << format << ", ChunkSize "
302               << config->audio_chunksize << "): " << SDL_GetError() << "\n";
303     return;
304   }
305   sound_manager->set_audio_device_available(true);
306   sound_manager->enable_sound(config->sound_enabled);
307   sound_manager->enable_music(config->music_enabled);
308   
309   if(Mix_AllocateChannels(config->audio_voices) < 0) {
310     std::cerr << "Couldn't allocate '" << config->audio_voices << "' audio voices: "
311               << SDL_GetError() << "\n";
312     return;
313   }
314 }
315
316 static void quit_audio()
317 {
318   if(sound_manager) {
319     if(sound_manager->audio_device_available())
320       Mix_CloseAudio();
321
322     delete sound_manager;
323     sound_manager = 0;
324   }
325 }
326
327 int main(int argc, char** argv) 
328 {
329 #ifndef DEBUG // we want backtraces in debug mode so don't catch exceptions
330   try {
331 #endif
332     srand(time(0));
333     init_sdl();
334     main_controller = new JoystickKeyboardController();    
335     find_directories();
336     init_config();
337     init_tinygettext();
338     parse_commandline(argc, argv);
339     init_audio();
340     init_video();
341
342     setup_menu();
343     load_shared();
344     if(config->start_level != "") {
345       GameSession session(config->start_level, ST_GL_LOAD_LEVEL_FILE);
346       session.run();
347     } else {
348       // normal game
349       title();
350     }    
351     
352 #ifndef DEBUG
353   } catch(std::exception& e) {
354     std::cerr << "Unexpected exception: " << e.what() << std::endl;
355     return 1;
356   } catch(...) {
357     std::cerr << "Unexpected exception." << std::endl;
358     return 1;
359   }
360 #endif
361
362   free_menu();
363   unload_shared();
364 #ifdef DEBUG
365   Surface::debug_check();
366 #endif
367   quit_audio();
368
369   if(config)
370     config->save();
371   delete config;
372   delete main_controller;
373   SDL_Quit();
374   
375   return 0;
376 }