1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis 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 Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 // Sébastien Scieux (Zbuffer)
45 // Didier Pedreno (ScreenSaver Problem Fix)
46 //
47 // Copyright (c) 1999 ARKANE Studios SA. All rights reserved
48
49 #include "core/Application.h"
50
51 #include <stddef.h>
52 #include <algorithm>
53 #include <set>
54 #include <sstream>
55
56 #include <boost/algorithm/string/case_conv.hpp>
57 #include <boost/foreach.hpp>
58
59 #include "core/Config.h"
60 #include "core/GameTime.h"
61
62 #include "graphics/Renderer.h"
63
64 #include "io/fs/FilePath.h"
65 #include "io/fs/Filesystem.h"
66 #include "io/fs/SystemPaths.h"
67 #include "io/log/Logger.h"
68
69 #include "math/Random.h"
70
71 #include "platform/Platform.h"
72
73 #include "window/RenderWindow.h"
74
75 using std::max;
76 using std::string;
77
78 //-----------------------------------------------------------------------------
79 long EERIEMouseButton = 0;
80 long EERIEMouseGrab = 0;
81
82 Application * mainApp = 0;
83 float FPS;
84 LightMode ModeLight = 0;
85 ViewModeFlags ViewMode = 0;
86
87 static int iCurrZBias;
88
89 //*************************************************************************************
90 // Application()
91 // Constructor
92 //*************************************************************************************
Application()93 Application::Application() : m_MainWindow(NULL) {
94 m_bReady = true;
95 m_RunLoop = true;
96 }
97
~Application()98 Application::~Application() {
99
100 }
101
Initialize()102 bool Application::Initialize() {
103
104 bool init;
105
106 init = InitConfig();
107 if(!init) {
108 LogCritical << "Failed to initialize the config subsystem.";
109 return false;
110 }
111
112 init = InitWindow();
113 if(!init) {
114 LogCritical << "Failed to initialize the windowing subsystem.";
115 return false;
116 }
117
118 init = InitInput();
119 if(!init) {
120 LogCritical << "Failed to initialize the input subsystem.";
121 return false;
122 }
123
124 init = InitSound();
125 if(!init) {
126 LogCritical << "Failed to initialize the sound subsystem.";
127 return false;
128 }
129
130 Random::seed();
131
132 return true;
133 }
134
Shutdown()135 void Application::Shutdown() {
136 delete m_MainWindow, m_MainWindow = NULL;
137 }
138
Quit()139 void Application::Quit() {
140 m_RunLoop = false;
141 }
142
migrateFilenames(fs::path path,bool is_dir)143 static bool migrateFilenames(fs::path path, bool is_dir) {
144
145 string name = path.filename();
146 string lowercase = boost::to_lower_copy(name);
147
148 bool migrated = true;
149
150 if(lowercase != name) {
151
152 fs::path dst = path.parent() / lowercase;
153
154 LogInfo << "Renaming " << path << " to " << dst.filename() << "";
155
156 if(fs::rename(path, dst)) {
157 path = dst;
158 } else {
159 migrated = false;
160 }
161 }
162
163 if(is_dir) {
164 for(fs::directory_iterator it(path); !it.end(); ++it) {
165 migrated &= migrateFilenames(path / it.name(), it.is_directory());
166 }
167 }
168
169 return migrated;
170 }
171
migrateFilenames(const fs::path & configFile)172 static bool migrateFilenames(const fs::path & configFile) {
173
174 LogInfo << "Changing filenames to lowercase...";
175
176 static const char * files[] = { "cfg.ini", "cfg_default.ini",
177 "sfx.pak", "loc.pak", "data2.pak", "data.pak", "speech.pak", "loc_default.pak", "speech_default.pak",
178 "save", "editor", "game", "graph", "localisation", "misc", "sfx", "speech" };
179
180 std::set<string> fileset;
181 for(size_t i = 0; i < ARRAY_SIZE(files); i++) {
182 fileset.insert(files[i]);
183 }
184
185 bool migrated = true;
186
187 for(fs::directory_iterator it(fs::paths.user); !it.end(); ++it) {
188 string file = it.name();
189 if(fileset.find(boost::to_lower_copy(file)) != fileset.end()) {
190 migrated &= migrateFilenames(fs::paths.user / file, it.is_directory());
191 }
192 }
193
194 if(!migrated) {
195 LogCritical << "Could not rename all files to lowercase, please do so manually and set migration=1 under [misc] in " << configFile;
196 }
197
198 return migrated;
199 }
200
InitConfig()201 bool Application::InitConfig() {
202
203 // Initialize config first, before anything else.
204 fs::path configFile = fs::paths.config / "cfg.ini";
205
206 config.setOutputFile(configFile);
207
208 bool migrated = false;
209 if(!fs::exists(configFile)) {
210
211 migrated = migrateFilenames(configFile);
212 if(!migrated) {
213 return false;
214 }
215
216 fs::path oldConfigFile = fs::paths.user / "cfg.ini";
217 if(fs::exists(oldConfigFile)) {
218 if(!fs::rename(oldConfigFile, configFile)) {
219 LogWarning << "Could not move " << oldConfigFile << " to "
220 << configFile;
221 } else {
222 LogInfo << "Moved " << oldConfigFile << " to " << configFile;
223 }
224 }
225 }
226
227 LogInfo << "Using config file " << configFile;
228 if(!config.init(configFile)) {
229
230 fs::path file = fs::paths.find("cfg_default.ini");
231 if(config.init(file)) {
232 LogWarning << "Could not read config files cfg.ini and cfg_default.ini,"
233 << " using defaults.";
234 }
235
236 // Save a default config file so users have a chance to edit it even if we crash.
237 config.save();
238 }
239
240 Logger::configure(config.misc.debug);
241
242 if(!migrated && config.misc.migration < Config::CaseSensitiveFilenames) {
243 migrated = migrateFilenames(configFile);
244 if(!migrated) {
245 return false;
246 }
247 }
248 if(migrated) {
249 config.misc.migration = Config::CaseSensitiveFilenames;
250 }
251
252 if(!fs::create_directories(fs::paths.user / "save")) {
253 LogWarning << "Failed to create save directory";
254 }
255
256 return true;
257 }
258
259 //*************************************************************************************
260 // Pause()
261 // Called in to toggle the pause state of the app. This function
262 // brings the GDI surface to the front of the display, so drawing
263 // output like message boxes and menus may be displayed.
264 //*************************************************************************************
Pause(bool bPause)265 void Application::Pause(bool bPause)
266 {
267 static u32 dwAppPausedCount = 0L;
268
269 dwAppPausedCount += (bPause ? +1 : -1);
270 m_bReady = (dwAppPausedCount ? false : true);
271
272 // Handle the first pause request (of many, nestable pause requests)
273 if (bPause && (1 == dwAppPausedCount))
274 {
275 // Get a surface for the GDI
276 //if (m_pFramework)
277 // m_pFramework->FlipToGDISurface(true); TODO
278 }
279 }
280
281 //*************************************************************************************
282 //*************************************************************************************
CalcFPS(bool reset)283 void CalcFPS(bool reset)
284 {
285 static float fFPS = 0.0f;
286 static float fLastTime = 0.0f;
287 static u32 dwFrames = 0L;
288
289 if (reset)
290 {
291 dwFrames = 0;
292 fLastTime = 0.f;
293 FPS = fFPS = 7.f * FPS;
294 }
295 else
296 {
297 float tmp;
298
299 // Keep track of the time lapse and frame count
300 float fTime = arxtime.get_updated(false) * 0.001f; // Get current time in seconds
301 ++dwFrames;
302
303 tmp = fTime - fLastTime;
304
305 // Update the frame rate once per second
306 if (tmp > 1.f)
307 {
308 FPS = fFPS = dwFrames / tmp ;
309 fLastTime = fTime;
310 dwFrames = 0L;
311 }
312 }
313 }
314
SetZBias(int _iZBias)315 void SetZBias(int _iZBias)
316 {
317 if (_iZBias < 0)
318 {
319 _iZBias = 0;
320 _iZBias = max(iCurrZBias, -_iZBias);
321 }
322
323 if (_iZBias == iCurrZBias)
324 return;
325
326 iCurrZBias = _iZBias;
327
328 GRenderer->SetDepthBias(iCurrZBias);
329 }
330