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