fix redraw at top-left of map
[Tsunagari.git] / src / main.cpp
blob095e84aaee7259b56e2605c55ef82d09c525388c
1 /******************************
2 ** Tsunagari Tile Engine **
3 ** main.cpp **
4 ** Copyright 2011 OmegaSDG **
5 ******************************/
7 #include <iostream>
8 #include <fstream>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string>
12 #include <string.h>
14 #include <boost/config.hpp>
15 #include <boost/program_options.hpp>
16 #include <boost/program_options/detail/config_file.hpp>
17 #include <boost/program_options/parsers.hpp>
18 #include <boost/scoped_ptr.hpp>
19 #include <boost/shared_ptr.hpp>
20 #include <libxml/parser.h>
22 #include "common.h"
23 #include "config.h"
24 #include "cmd.h"
25 #include "log.h"
26 #include "window.h"
28 #ifdef _WINDOWS
29 #include <Windows.h>
30 #endif
32 /* Output compiled-in engine defaults. */
33 static void defaultsQuery()
35 std::cerr << "CLIENT_CONF_FILE: "
36 << CLIENT_CONF_FILE << std::endl;
37 std::cerr << "MESSAGE_MODE: ";
38 if (MESSAGE_MODE == MM_DEBUG)
39 std::cerr << "MM_DEBUG" << std::endl;
40 else if (MESSAGE_MODE == MM_DEVELOPER)
41 std::cerr << "MM_DEVELOPER" << std::endl;
42 else
43 std::cerr << "MM_ERROR" << std::endl;
44 std::cerr << "GAME_MODE: ";
45 if (GAME_MODE == JUMP_MOVE)
46 std::cerr << "JUMP_MOVE" << std::endl;
47 else if (GAME_MODE == SLIDE_MOVE)
48 std::cerr << "SLIDE_MOVE" << std::endl;
49 else
50 std::cerr << "FREE_MOVE" << std::endl;
51 std::cerr << "ROGUELIKE_PERSIST_DELAY_INIT: "
52 << ROGUELIKE_PERSIST_DELAY_INIT << std::endl;
53 std::cerr << "ROGUELIKE_PERSIST_DELAY_CONSECUTIVE: "
54 << ROGUELIKE_PERSIST_DELAY_CONSECUTIVE << std::endl;
55 std::cerr << "CACHE_EMPTY_TTL: "
56 << CACHE_EMPTY_TTL << std::endl;
57 std::cerr << "CACHE_MAX_SIZE: "
58 << CACHE_MAX_SIZE << std::endl;
61 /**
62 * Load the values we need to start initializing the game from an ini file.
64 * We need to know what size window to create and which World to load. This
65 * information will be stored in an ini file which we parse here.
67 * @param filename Name of the ini file to load from.
69 * @return ClientValues object if successful
71 static ClientValues* parseConfig(const char* filename)
73 namespace pod = boost::program_options::detail;
75 ClientValues* conf = new ClientValues;
77 conf->cache_enabled = CACHE_EMPTY_TTL && CACHE_MAX_SIZE;
79 std::ifstream config(filename);
80 if (!config) {
81 Log::err(filename, "could not parse config");
82 delete conf;
83 return NULL;
86 std::set<std::string> options;
87 std::map<std::string, std::string> parameters;
88 options.insert("*");
90 for (pod::config_file_iterator i(config, options), e ; i != e; ++i) {
91 parameters[i->string_key] = i->value[0];
94 if (parameters["engine.world"].empty()) {
95 Log::err(filename, "\"[engine] world\" option expected");
96 return NULL;
98 else
99 conf->world = parameters["engine.world"];
101 if (parameters["engine.dtddir"].empty()) {
102 Log::err(filename, "\"[engine] dtddir\" option expected");
103 return NULL;
105 else
106 conf->dtdDir = parameters["engine.dtddir"];
108 if (parameters["window.width"].empty()) {
109 Log::err(filename, "\"[window] width\" option expected");
110 return NULL;
112 else
113 conf->windowsize.x = atoi(parameters["window.width"].c_str());
115 if (parameters["window.height"].empty()) {
116 Log::err(filename, "\"[window] height\" option expected");
117 return NULL;
119 else
120 conf->windowsize.y = atoi(parameters["window.height"].c_str());
122 if (parameters["window.fullscreen"].empty()) {
123 Log::err(filename, "\"[window] fullscreen\" option expected");
124 return NULL;
126 else
127 conf->fullscreen = parseBool(parameters["window.fullscreen"]);
129 if (!parameters["cache.enable"].empty()) {
130 if (parseBool(parameters["cache.enable"]))
131 conf->cache_enabled = true;
132 else
133 conf->cache_enabled = false;
136 if (parameters["cache.ttl"].empty())
137 conf->cache_ttl = CACHE_EMPTY_TTL;
138 else {
139 if (atoi(parameters["cache.ttl"].c_str()) == 0)
140 conf->cache_enabled = 0;
141 conf->cache_ttl = atoi(parameters["cache.ttl"].c_str());
144 if (parameters["cache.size"].empty())
145 conf->cache_size = CACHE_MAX_SIZE;
146 else {
147 if (atoi(parameters["cache.size"].c_str()) == 0)
148 conf->cache_enabled = 0;
149 conf->cache_size = atoi(parameters["cache.size"].c_str());
152 if (parameters["engine.loglevel"].empty())
153 conf->loglevel = MESSAGE_MODE;
154 else if (parameters["engine.loglevel"] == "error" ||
155 parameters["engine.loglevel"] == "Error" ||
156 parameters["engine.loglevel"] == "ERROR")
157 conf->loglevel = MM_SILENT;
158 else if (parameters["engine.loglevel"] == "devel" ||
159 parameters["engine.loglevel"] == "Devel" ||
160 parameters["engine.loglevel"] == "DEVEL")
161 conf->loglevel = MM_DEVELOPER;
162 else if (parameters["engine.loglevel"] == "debug" ||
163 parameters["engine.loglevel"] == "Debug" ||
164 parameters["engine.loglevel"] == "DEBUG")
165 conf->loglevel = MM_DEBUG;
166 else {
167 Log::err(filename, "unknown value for \"[engine] loglevel\", using default");
168 conf->loglevel = MESSAGE_MODE;
171 return conf;
174 /* Parse and process command line options and arguments. */
175 static bool parseCommandLine(int argc, char* argv[], ClientValues* conf)
177 CommandLineOptions cmd(argc, argv);
179 cmd.insert("-h", "--help", "", "Display this help message");
180 cmd.insert("-g", "--gameworld", "<world file>", "Game world to load");
181 cmd.insert("-c", "--config", "<config file>", "Client config file to use");
182 cmd.insert("-v", "--verbosity", "<error,devel,debug>", "Log message level");
183 cmd.insert("-t", "--cache-ttl", "<seconds>", "Cache time-to-live in seconds");
184 cmd.insert("-m", "--cache-size", "<megabytes>", "Cache size in megabytes");
185 cmd.insert("-s", "--size", "<WxH>", "Window dimensions");
186 cmd.insert("-f", "--fullscreen", "", "Run in fullscreen mode");
187 cmd.insert("-w", "--window", "", "Run in windowed mode");
188 cmd.insert("-q", "--query", "", "Query compiled-in engine defaults");
189 cmd.insert("", "--version", "", "Print the engine version string");
191 if (!cmd.parse()) {
192 Log::err(argv[0], "bad command line");
193 cmd.usage();
194 return false;
197 if (cmd.check("--help")) {
198 cmd.usage();
199 return false;
202 if (cmd.check("--version")) {
203 std::cerr << TSUNAGARI_RELEASE_VERSION << std::endl;
204 return false;
207 if (cmd.check("--query")) {
208 defaultsQuery();
209 return false;
212 if (cmd.check("--config")) {
213 delete conf;
214 conf = parseConfig(cmd.get("--config").c_str());
215 if (!conf) {
216 Log::err(cmd.get("--config"), "loading config failed");
217 return false;
221 if (cmd.check("--gameworld"))
222 conf->world = cmd.get("--gameworld");
224 if (cmd.check("--verbosity")) {
225 if (!cmd.get("--verbosity").compare("error"))
226 conf->loglevel = MM_SILENT;
227 else if (!cmd.get("--verbosity").compare("devel"))
228 conf->loglevel = MM_DEVELOPER;
229 else if (!cmd.get("--verbosity").compare("debug"))
230 conf->loglevel = MM_DEBUG;
231 else {
232 Log::err(argv[0], "invalid argument for --verbosity");
233 return false;
237 if (cmd.check("--cache-ttl")) {
238 conf->cache_ttl = atoi(cmd.get("--cache-ttl").c_str());
239 if (conf->cache_ttl == 0)
240 conf->cache_enabled = false;
243 if (cmd.check("--cache-size")) {
244 conf->cache_size = atoi(cmd.get("--cache-size").c_str());
245 if (conf->cache_size == 0)
246 conf->cache_enabled = false;
249 if (cmd.check("--size")) {
250 std::vector<std::string> dim = splitStr(cmd.get("--size"), "x");
251 if (dim.size() != 2) {
252 Log::err(argv[0], "invalid argument for --size");
253 return false;
255 conf->windowsize.x = atoi(dim[0].c_str());
256 conf->windowsize.y = atoi(dim[1].c_str());
259 if (cmd.check("--fullscreen") && cmd.check("--window")) {
260 Log::err(argv[0], "--fullscreen and --window mutually exclusive");
261 return false;
264 if (cmd.check("--fullscreen"))
265 conf->fullscreen = true;
267 if (cmd.check("--window"))
268 conf->fullscreen = false;
270 return true;
273 static void initLibraries()
276 * This initializes the library and checks for potential ABI mismatches
277 * between the version it was compiled for and the actual shared
278 * library used.
280 LIBXML_TEST_VERSION
283 static void cleanupLibraries()
285 // Clean the XML library.
286 xmlCleanupParser();
290 * Load client config and instantiate window.
292 * The client config tells us our window parameters along with which World
293 * we're going to load. The GameWindow class then loads and plays the game.
295 int main(int argc, char** argv)
297 #if _WINDOWS /* Fix console output on Windows */
298 if (AttachConsole(ATTACH_PARENT_PROCESS)) {
299 freopen("CONOUT$","wb",stdout);
300 freopen("CONOUT$","wb",stderr);
302 #endif
304 initLibraries();
306 ClientValues* conf = parseConfig(CLIENT_CONF_FILE);
308 if (!parseCommandLine(argc, argv, conf)) {
309 delete conf;
310 return 1;
313 if (conf && conf->loglevel)
314 Log::setMode(conf->loglevel);
316 if (!conf) {
317 Log::err(CLIENT_CONF_FILE, "loading config failed");
318 delete conf;
319 return 1;
322 GameWindow window((unsigned)conf->windowsize.x,
323 (unsigned)conf->windowsize.y,
324 conf->fullscreen);
326 if (window.init(conf))
327 window.show();
329 delete conf;
331 cleanupLibraries();
333 return 0;