aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/irrlicht-1.8.1/examples/25.XmlHandling/main.cpp
blob: 3eb261deb0fe718a71166c073d7f00a930f29786 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
/** Example 025 Xml Handling

Demonstrates loading and saving of configurations via XML

@author Y.M. Bosman	\<yoran.bosman@gmail.com\>

This demo features a fully usable system for configuration handling. The code
can easily be integrated into own apps.

*/

#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif


/* SettingManager class.

This class loads and writes the settings and manages the options.

The class makes use of irrMap which is a an associative arrays using a
red-black tree it allows easy mapping of a key to a value, along the way there
is some information on how to use it.
*/

class SettingManager
{
public:

	// Construct setting managers and set default settings
	SettingManager(const stringw& settings_file): SettingsFile(settings_file), NullDevice(0)
	{
		// Irrlicht null device, we want to load settings before we actually created our device, therefore, nulldevice
		NullDevice = irr::createDevice(irr::video::EDT_NULL);

		//DriverOptions is an irrlicht map,
		//we can insert values in the map in two ways by calling insert(key,value) or by using the [key] operator
		//the [] operator overrides values if they already exist
		DriverOptions.insert(L"Software", EDT_SOFTWARE);
		DriverOptions.insert(L"OpenGL", EDT_OPENGL);
		DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9);

		//some resolution options
		ResolutionOptions.insert(L"640x480", dimension2du(640,480));
		ResolutionOptions.insert(L"800x600", dimension2du(800,600));
		ResolutionOptions.insert(L"1024x768", dimension2du(1024,768));

		//our preferred defaults
		SettingMap.insert(L"driver", L"Direct3D9");
		SettingMap.insert(L"resolution", L"640x480");
		SettingMap.insert(L"fullscreen", L"0"); //0 is false
	}

	// Destructor, you could store settings automatically on exit of your
	// application if you wanted to in our case we simply drop the
	// nulldevice
	~SettingManager()
	{
		if (NullDevice)
		{
			NullDevice->closeDevice();
			NullDevice->drop();
		}
	};

	/*
	Load xml from disk, overwrite default settings
	The xml we are trying to load has the following structure
	settings nested in sections nested in the root node, like so
	<pre>
		<?xml version="1.0"?>
		<mygame>
			<video>
				<setting name="driver" value="Direct3D9" />
				<setting name="fullscreen" value="0" />
				<setting name="resolution" value="1024x768" />
			</video>
		</mygame>
	</pre>
	*/
	bool load()
	{
		//if not able to create device don't attempt to load
		if (!NullDevice)
			return false;

		irr::io::IXMLReader* xml = NullDevice->getFileSystem()->createXMLReader(SettingsFile);	//create xml reader
		if (!xml)
			return false;

		const stringw settingTag(L"setting"); //we'll be looking for this tag in the xml
		stringw currentSection; //keep track of our current section
		const stringw videoTag(L"video"); //constant for videotag

		//while there is more to read
		while (xml->read())
		{
			//check the node type
			switch (xml->getNodeType())
			{
				//we found a new element
				case irr::io::EXN_ELEMENT:
				{
					//we currently are in the empty or mygame section and find the video tag so we set our current section to video
					if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName()))
					{
						currentSection = videoTag;
					}
					//we are in the video section and we find a setting to parse
					else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() ))
					{
						//read in the key
						stringw key = xml->getAttributeValueSafe(L"name");
						//if there actually is a key to set
						if (!key.empty())
						{
							//set the setting in the map to the value,
							//the [] operator overrides values if they already exist or inserts a new key value
							//pair into the settings map if it was not defined yet
							SettingMap[key] = xml->getAttributeValueSafe(L"value");
						}
					}

					//..
					// You can add your own sections and tags to read in here
					//..
				}
				break;

				//we found the end of an element
				case irr::io::EXN_ELEMENT_END:
					//we were at the end of the video section so we reset our tag
					currentSection=L"";
				break;
			}
		}

		// don't forget to delete the xml reader
		xml->drop();

		return true;
	}

	// Save the xml to disk. We use the nulldevice.
	bool save()
	{

		//if not able to create device don't attempt to save
		if (!NullDevice)
			return false;

		//create xml writer
		irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter( SettingsFile );
		if (!xwriter)
			return false;

		//write out the obligatory xml header. Each xml-file needs to have exactly one of those.
		xwriter->writeXMLHeader();

		//start element	mygame, you replace the label "mygame" with anything you want
		xwriter->writeElement(L"mygame");
		xwriter->writeLineBreak();					//new line

		//start section with video settings
		xwriter->writeElement(L"video");
		xwriter->writeLineBreak();					//new line

		// getIterator gets us a pointer to the first node of the settings map
		// every iteration we increase the iterator which gives us the next map node
		// until we reach the end we write settings one by one by using the nodes key and value functions
		map<stringw, stringw>::Iterator i = SettingMap.getIterator();
		for(; !i.atEnd(); i++)
		{
			//write element as <setting name="key" value="x" />
			//the second parameter indicates this is an empty element with no children, just attributes
			xwriter->writeElement(L"setting",true, L"name", i->getKey().c_str(), L"value",i->getValue().c_str() );
			xwriter->writeLineBreak();
		}
		xwriter->writeLineBreak();

		//close video section
		xwriter->writeClosingTag(L"video");
		xwriter->writeLineBreak();

		//..
		// You can add writing sound settings, savegame information etc
		//..

		//close mygame section
		xwriter->writeClosingTag(L"mygame");

		//delete xml writer
		xwriter->drop();

		return true;
	}

	// Set setting in our manager
	void setSetting(const stringw& name, const stringw& value)
	{
		SettingMap[name]=value;
	}

	// set setting overload to quickly assign integers to our setting map
	void setSetting(const stringw& name, s32 value)
	{
		SettingMap[name]=stringw(value);
	}

	// Get setting as string
	stringw getSetting(const stringw& key) const
	{
		//the find function or irrmap returns a pointer to a map Node
		//if the key can be found, otherwise it returns null
		//the map node has the function getValue and getKey, as we already know the key, we return node->getValue()
		map<stringw, stringw>::Node* n = SettingMap.find(key);
		if (n)
			return n->getValue();
		else
			return L"";
	}

	//
	bool getSettingAsBoolean(const stringw& key ) const
	{
		stringw s = getSetting(key);
		if (s.empty())
			return false;
		return s.equals_ignore_case(L"1");
	}

	//
	s32 getSettingAsInteger(const stringw& key) const
	{
		//we implicitly cast to string instead of stringw because strtol10 does not accept wide strings
		const stringc s = getSetting(key);
		if (s.empty())
			return 0;

		return strtol10(s.c_str());
	}

public:
	map<stringw, s32> DriverOptions; //available options for driver config
	map<stringw, dimension2du> ResolutionOptions; //available options for resolution config
private:
	SettingManager(const SettingManager& other); // defined but not implemented
	SettingManager& operator=(const SettingManager& other); // defined but not implemented

	map<stringw, stringw> SettingMap; //current config

	stringw SettingsFile; // location of the xml, usually the
	irr::IrrlichtDevice* NullDevice;
};

/*
Application context for global variables
*/
struct SAppContext
{
	SAppContext()
		: Device(0),Gui(0), Driver(0), Settings(0), ShouldQuit(false),
		ButtonSave(0), ButtonExit(0), ListboxDriver(0),
		ListboxResolution(0), CheckboxFullscreen(0)
	{
	}

	~SAppContext()
	{
		if (Settings)
			delete Settings;

		if (Device)
		{
			Device->closeDevice();
			Device->drop();
		}
	}

	IrrlichtDevice* Device;
	IGUIEnvironment* Gui;
	IVideoDriver* Driver;
	SettingManager* Settings;
	bool ShouldQuit;

	//settings dialog
	IGUIButton* ButtonSave;
	IGUIButton* ButtonExit;
	IGUIListBox* ListboxDriver;
	IGUIListBox* ListboxResolution;
	IGUICheckBox* CheckboxFullscreen;
};

/*
	A typical event receiver.
*/
class MyEventReceiver : public IEventReceiver
{
public:
	MyEventReceiver(SAppContext & a) : App(a) { }

	virtual bool OnEvent(const SEvent& event)
	{
		if (event.EventType == EET_GUI_EVENT )
		{
			switch ( event.GUIEvent.EventType )
			{
				//handle button click events
				case EGET_BUTTON_CLICKED:
				{
					//Our save button was called so we obtain the settings from our dialog and save them
					if ( event.GUIEvent.Caller == App.ButtonSave )
					{
						//if there is a selection write it
						if ( App.ListboxDriver->getSelected() != -1)
							App.Settings->setSetting(L"driver",	App.ListboxDriver->getListItem(App.ListboxDriver->getSelected()));

						//if there is a selection write it
						if ( App.ListboxResolution->getSelected() != -1)
							App.Settings->setSetting(L"resolution", App.ListboxResolution->getListItem(App.ListboxResolution->getSelected()));

						App.Settings->setSetting(L"fullscreen",	App.CheckboxFullscreen->isChecked());


						if (App.Settings->save())
						{
							App.Gui->addMessageBox(L"settings save",L"settings saved, please restart for settings to change effect","",true);
						}
					}
					// cancel/exit button clicked, tell the application to exit
					else if ( event.GUIEvent.Caller == App.ButtonExit)
					{
						App.ShouldQuit = true;
					}
				}
				break;
			}
		}

		return false;
	}

private:
	SAppContext & App;
};


/*
Function to create a video settings dialog
This dialog shows the current settings from the configuration xml and allows them to be changed
*/
void createSettingsDialog(SAppContext& app)
{
	// first get rid of alpha in gui
	for (irr::s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
	{
		irr::video::SColor col = app.Gui->getSkin()->getColor((irr::gui::EGUI_DEFAULT_COLOR)i);
		col.setAlpha(255);
		app.Gui->getSkin()->setColor((irr::gui::EGUI_DEFAULT_COLOR)i, col);
	}

	//create video settings windows
	gui::IGUIWindow* windowSettings = app.Gui->addWindow(rect<s32>(10,10,400,400),true,L"Videosettings");
	app.Gui->addStaticText (L"Select your desired video settings", rect< s32 >(10,20, 200, 40), false, true, windowSettings);

	// add listbox for driver choice
	app.Gui->addStaticText (L"Driver", rect< s32 >(10,50, 200, 60), false, true, windowSettings);
	app.ListboxDriver = app.Gui->addListBox(rect<s32>(10,60,220,120), windowSettings, 1,true);

	//add all available options to the driver choice listbox
	map<stringw, s32>::Iterator i = app.Settings->DriverOptions.getIterator();
	for(; !i.atEnd(); i++)
		app.ListboxDriver->addItem(i->getKey().c_str());

	//set currently selected driver
	app.ListboxDriver->setSelected(app.Settings->getSetting("driver").c_str());

	// add listbox for resolution choice
	app.Gui->addStaticText (L"Resolution", rect< s32 >(10,130, 200, 140), false, true, windowSettings);
	app.ListboxResolution = app.Gui->addListBox(rect<s32>(10,140,220,200), windowSettings, 1,true);

	//add all available options to the resolution listbox
	map<stringw, dimension2du>::Iterator ri = app.Settings->ResolutionOptions.getIterator();
	for(; !ri.atEnd(); ri++)
		app.ListboxResolution->addItem(ri->getKey().c_str());

	//set currently selected resolution
	app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str());

	//add checkbox to toggle fullscreen, initially set to loaded setting
	app.CheckboxFullscreen = app.Gui->addCheckBox(
			app.Settings->getSettingAsBoolean("fullscreen"),
			rect<s32>(10,220,220,240), windowSettings, -1,
			L"Fullscreen");

	//last but not least add save button
	app.ButtonSave = app.Gui->addButton(
			rect<s32>(80,250,150,270), windowSettings, 2,
			L"Save video settings");

	//exit/cancel button
	app.ButtonExit = app.Gui->addButton(
			rect<s32>(160,250,240,270), windowSettings, 2,
			L"Cancel and exit");
}

/*
The main function. Creates all objects and does the XML handling.
*/
int main()
{
	//create new application context
	SAppContext app;

	//create device creation parameters that can get overwritten by our settings file
	SIrrlichtCreationParameters param;
	param.DriverType = EDT_SOFTWARE;
	param.WindowSize.set(640,480);

	// Try to load config.
	// I leave it as an exercise of the reader to store the configuration in the local application data folder,
	// the only logical place to store config data for games. For all other operating systems I redirect to your manuals
	app.Settings = new SettingManager("../../media/settings.xml");
	if ( !app.Settings->load() )
	{
		// ...
		// Here add your own exception handling, for now we continue because there are defaults set in SettingManager constructor
		// ...
	}
	else
	{
		//settings xml loaded from disk,

		//map driversetting to driver type and test if the setting is valid
		//the DriverOptions map contains string representations mapped to to irrlicht E_DRIVER_TYPE enum
		//e.g "direct3d9" will become 4
		//see DriverOptions in the settingmanager class for details
		map<stringw, s32>::Node* driver = app.Settings->DriverOptions.find( app.Settings->getSetting("driver") );

		if (driver)
		{
			if ( irr::IrrlichtDevice::isDriverSupported( static_cast<E_DRIVER_TYPE>( driver->getValue() )))
			{
				// selected driver is supported, so we use it.
				param.DriverType = static_cast<E_DRIVER_TYPE>( driver->getValue());
			}
		}

		//map resolution setting to dimension in a similar way as demonstrated above
		map<stringw, dimension2du>::Node* res = app.Settings->ResolutionOptions.find( app.Settings->getSetting("resolution") );
		if (res)
		{
			param.WindowSize = res->getValue();
		}

		//get fullscreen setting from config
		param.Fullscreen = app.Settings->getSettingAsBoolean("fullscreen");
	}

	//create the irrlicht device using the settings
	app.Device = createDeviceEx(param);
	if (app.Device == 0)
	{
		// You can add your own exception handling on driver failure
		exit(0);
	}

	app.Device->setWindowCaption(L"Xmlhandling - Irrlicht engine tutorial");
	app.Driver	= app.Device->getVideoDriver();
	app.Gui		= app.Device->getGUIEnvironment();

	createSettingsDialog(app);

	//set event receiver so we can respond to gui events
	MyEventReceiver receiver(app);
	app.Device->setEventReceiver(&receiver);

	//enter main loop
	while (!app.ShouldQuit && app.Device->run())
	{
		if (app.Device->isWindowActive())
		{
			app.Driver->beginScene(true, true, SColor(0,200,200,200));
			app.Gui->drawAll();
			app.Driver->endScene();
		}
		app.Device->sleep(10);
	}

	//app destroys device in destructor

	return 0;
}

/*
**/