1 /******************************
2 ** Tsunagari Tile Engine **
4 ** Copyright 2011 OmegaSDG **
5 ******************************/
13 #include <boost/scoped_ptr.hpp>
14 #include <boost/shared_ptr.hpp>
15 #include <libxml/parser.h>
16 #include <libxml/tree.h>
24 #ifndef LIBXML_TREE_ENABLED
25 # error Tree must be enabled in libxml2
33 struct poptOption optionsTable
[] = {
34 {"conf", 'c', POPT_ARG_STRING
, &customConf
, 'c', "Client config file to use", "CONFFILE"},
35 {"verbosity", 'v', POPT_ARG_STRING
, &verbosity
, 'v', "Log message level", "ERROR,DEVEL,DEBUG"},
36 {"cache-ttl", 't', POPT_ARG_INT
, &verbosity
, 't', "Resource cache time-to-live", "SECONDS"},
37 {"size", 's', POPT_ARG_STRING
, &size
, 's', "Window dimensions", "WxH"},
38 {"fullscreen", 'f', POPT_ARG_NONE
, 0, 'f', "Run in fullscreen mode", NULL
},
39 {"window", 'w', POPT_ARG_NONE
, 0, 'w', "Run in windowed mode", NULL
},
40 {"query", 'q', POPT_ARG_NONE
, 0, 'q', "Query compiled-in engine defaults", NULL
},
46 * Values needed prior to creating the GameWindow.
54 message_mode_t loglevel
;
57 static void xmlErrorCb(void*, const char* msg
, ...)
62 sprintf(buf
, msg
, va_arg(ap
, char*));
63 Log::err(CLIENT_CONF_FILE
, buf
);
68 * Load the values we need to start initializing the game from an XML file.
70 * We need to know what size window to create and which World to load. This
71 * information will be stored in a XML file which we parse here.
73 * @param filename Name of the XML-encoded file to load from.
75 * @return ClientValues object if successful
77 static ClientValues
* parseConfig(const char* filename
)
82 doc
= xmlReadFile(filename
, NULL
, XML_PARSE_NOBLANKS
);
85 Log::err(filename
, "Could not parse file");
89 boost::shared_ptr
<void> alwaysFreeTheDoc(doc
, xmlFreeDoc
);
92 ctxt
.error
= xmlErrorCb
;
93 if (!xmlValidateDocument(&ctxt
, doc
)) {
94 Log::err(filename
, "XML document does not follow DTD");
98 root
= xmlDocGetRootElement(doc
);
99 xmlNode
* node
= root
->xmlChildrenNode
; // <client>
102 /* Extract from XML object:
103 * - name of World to load
104 * - width, height, fullscreen-ness of Window
106 ClientValues
* conf
= new ClientValues
;
107 node
= node
->xmlChildrenNode
;
108 while (node
!= NULL
) {
109 if (!xmlStrncmp(node
->name
, BAD_CAST("world"), 6)) {
110 xmlChar
* str
= xmlNodeGetContent(node
);
111 conf
->world
= (char*)str
;
113 else if (!xmlStrncmp(node
->name
, BAD_CAST("window"), 7)) {
114 str
= xmlGetProp(node
, BAD_CAST("x"));
115 conf
->windowsize
.x
= atol((char*)str
); // atol
117 str
= xmlGetProp(node
, BAD_CAST("y"));
118 conf
->windowsize
.y
= atol((char*)str
); // atol
120 str
= xmlGetProp(node
, BAD_CAST("fullscreen"));
121 conf
->fullscreen
= parseBool((char*)str
);
123 else if (!xmlStrncmp(node
->name
, BAD_CAST("cache"), 6)) {
124 str
= xmlGetProp(node
, BAD_CAST("enabled"));
125 conf
->cache_enabled
= (tern
)parseBool((char*)str
);
126 str
= xmlGetProp(node
, BAD_CAST("ttl"));
127 conf
->cache_ttl
= (tern
)parseBool((char*)str
);
129 else if (!xmlStrncmp(node
->name
, BAD_CAST("logging"), 8)) {
130 str
= xmlGetProp(node
, BAD_CAST("level"));
131 if (!strcmp((char*)str
, "error"))
132 conf
->loglevel
= MM_SILENT
;
133 else if (!strcmp((char*)str
, "devel"))
134 conf
->loglevel
= MM_DEVELOPER
;
135 else if (!strcmp((char*)str
, "debug"))
136 conf
->loglevel
= MM_DEBUG
;
144 static void initLibraries()
147 * This initializes the library and checks for potential ABI mismatches
148 * between the version it was compiled for and the actual shared
154 static void cleanupLibraries()
156 // Clean the XML library.
160 static void usage(poptContext optCon
, const char* msg
)
162 poptPrintUsage(optCon
, stderr
, 0);
167 static void defaultsQuery()
169 std::cerr
<< "CLIENT_CONF_FILE: "
170 << CLIENT_CONF_FILE
<< std::endl
;
171 std::cerr
<< "MESSAGE_MODE: ";
172 if (MESSAGE_MODE
== MM_DEBUG
)
173 std::cerr
<< "MM_DEBUG" << std::endl
;
174 else if (MESSAGE_MODE
== MM_DEVELOPER
)
175 std::cerr
<< "MM_DEVELOPER" << std::endl
;
177 std::cerr
<< "MM_ERROR" << std::endl
;
178 std::cerr
<< "ROGUELIKE_PERSIST_DELAY_INIT: "
179 << ROGUELIKE_PERSIST_DELAY_INIT
<< std::endl
;
180 std::cerr
<< "ROGUELIKE_PERSIST_DELAY_CONSECUTIVE: "
181 << ROGUELIKE_PERSIST_DELAY_CONSECUTIVE
<< std::endl
;
182 std::cerr
<< "CACHE_EMPTY_TTL: "
183 << CACHE_EMPTY_TTL
<< std::endl
;
187 * Load client config and instantiate window.
189 * The client config tells us our window parameters along with which World
190 * we're going to load. The GameWindow class then loads and plays the game.
192 int main(int argc
, char** argv
)
196 ClientValues
* conf
= parseConfig(CLIENT_CONF_FILE
);
197 poptContext optCon
= poptGetContext(NULL
, argc
, (const char**)argv
, optionsTable
, 0);
198 poptSetOtherOptionHelp(optCon
, "[WORLD FILE]");
200 std::vector
<std::string
> dimensions
;
202 while ((c
= poptGetNextOpt(optCon
)) >= 0) {
206 conf
= parseConfig(customConf
);
211 if (!strcmp(verbosity
, "error"))
212 conf
->loglevel
= MM_SILENT
;
213 else if (!strcmp(verbosity
, "devel"))
214 conf
->loglevel
= MM_DEVELOPER
;
215 else if (!strcmp(verbosity
, "debug"))
216 conf
->loglevel
= MM_DEBUG
;
218 usage(optCon
, "Log level must be one of (error, devel, debug)\n");
221 // FIXME: requires resource cache
224 dimensions
= splitStr(size
, "x");
225 if (dimensions
.size() != 2)
226 usage(optCon
, "Dimensions must be in form WxH: e.g. 800x600\n");
227 conf
->windowsize
.x
= atoi(dimensions
[0].c_str());
228 conf
->windowsize
.y
= atoi(dimensions
[1].c_str());
231 conf
->fullscreen
= true;
234 conf
->fullscreen
= false;
246 const char* customWorld
= poptGetArg(optCon
);
248 conf
->world
= customWorld
;
249 if (poptPeekArg(optCon
))
250 usage(optCon
, "Specify a single world: e.g. babysfirst.world\n");
251 poptFreeContext(optCon
);
254 Log::setMode(conf
->loglevel
);
256 GameWindow
window((unsigned)conf
->windowsize
.x
,
257 (unsigned)conf
->windowsize
.y
,
259 if (window
.init(conf
->world
))