Loading...
Searching...
No Matches
Tutorial 25: Xml Handling

Demonstrates loading and saving of configurations via XML

Author
  Y.M. Bosman <yoran.nosp@m..bos.nosp@m.man@g.nosp@m.mail.nosp@m..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
irrlicht.h
Main header file of the irrlicht, the only file needed to include.
irr::core
Basic classes such as vectors, planes, arrays, lists, and so on can be found in this namespace.
Definition aabbox3d.h:15
irr::gui
The gui namespace contains useful classes for easy creation of a graphical user interface.
Definition EGUIAlignment.h:11
irr::io
This namespace provides interfaces for input/output: Reading and writing files, accessing zip archive...
Definition EAttributes.h:11
irr::scene
All scene management can be found in this namespace: Mesh loading, special scene nodes like octrees a...
Definition CDynamicMeshBuffer.h:16
irr::video
The video namespace contains classes for accessing the video driver. All 2d and 3d rendering is done ...
Definition EDriverFeatures.h:11
irr
Everything in the Irrlicht Engine can be found in this namespace.
Definition aabbox3d.h:13

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();
}
};
irr::video::EDT_NULL
@ EDT_NULL
Null driver, useful for applications to run the engine without visualisation.
Definition EDriverTypes.h:19
irr::createDevice
IRRLICHT_API IrrlichtDevice *IRRCALLCONV createDevice(video::E_DRIVER_TYPE deviceType=video::EDT_SOFTWARE, const core::dimension2d< u32 > &windowSize=(core::dimension2d< u32 >(640, 480)), u32 bits=16, bool fullscreen=false, bool stencilbuffer=false, bool vsync=false, IEventReceiver *receiver=0)
Creates an Irrlicht device. The Irrlicht device is the root object for using the engine.

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:

        <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;
};
irr::IReferenceCounted::drop
bool drop() const
Drops the object. Decrements the reference counter by one.
Definition IReferenceCounted.h:116
irr::IrrlichtDevice
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
Definition IrrlichtDevice.h:44
irr::core::string::find
s32 find(const B *const str, const u32 start=0) const
finds another string in this string
Definition irrString.h:859
irr::io::IIrrXMLReader::read
virtual bool read()=0
Reads forward to the next xml node.
irr::io::IIrrXMLReader::getNodeType
virtual EXML_NODE getNodeType() const =0
Returns the type of the current XML node.
irr::io::IIrrXMLReader::getAttributeValueSafe
virtual const char_type * getAttributeValueSafe(const char_type *name) const =0
Returns the value of an attribute in a safe way.
irr::io::IIrrXMLReader::getNodeName
virtual const char_type * getNodeName() const =0
Returns the name of the current node.
irr::io::IXMLWriter
Interface providing methods for making it easier to write XML files.
Definition IXMLWriter.h:21
irr::io::IXMLWriter::writeElement
virtual void writeElement(const wchar_t *name, bool empty=false, const wchar_t *attr1Name=0, const wchar_t *attr1Value=0, const wchar_t *attr2Name=0, const wchar_t *attr2Value=0, const wchar_t *attr3Name=0, const wchar_t *attr3Value=0, const wchar_t *attr4Name=0, const wchar_t *attr4Value=0, const wchar_t *attr5Name=0, const wchar_t *attr5Value=0)=0
irr::io::IXMLWriter::writeXMLHeader
virtual void writeXMLHeader()=0
Writes an xml 1.0 header.
irr::io::IXMLWriter::writeClosingTag
virtual void writeClosingTag(const wchar_t *name)=0
Writes the closing tag for an element. Like "</foo>".
irr::io::IXMLWriter::writeLineBreak
virtual void writeLineBreak()=0
Writes a line break.
irr::core::strtol10
s32 strtol10(const char *in, const char **out=0)
Convert a simple string of base 10 digits into a signed 32 bit integer.
Definition fast_atof.h:90
irr::core::stringc
string< c8 > stringc
Typedef for character strings.
Definition irrString.h:1358
irr::core::stringw
string< wchar_t > stringw
Typedef for wide character strings.
Definition irrString.h:1361
irr::io::EXN_ELEMENT_END
@ EXN_ELEMENT_END
End of an xml element such as </foo>.
Definition irrXML.h:190
irr::io::EXN_ELEMENT
@ EXN_ELEMENT
An xml element such as <foo>.
Definition irrXML.h:187
irr::io::IXMLReader
IIrrXMLReader< wchar_t, IReferenceCounted > IXMLReader
An xml reader for wide characters, derived from IReferenceCounted.
Definition IXMLReader.h:19
irr::s32
signed int s32
32 bit signed variable.
Definition irrTypes.h:66

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;
};
irr::IEventReceiver
Interface of an object which can receive events.
Definition IEventReceiver.h:434
irr::EET_GUI_EVENT
@ EET_GUI_EVENT
An event of the graphical user interface.
Definition IEventReceiver.h:22
irr::SEvent::SGUIEvent::Caller
gui::IGUIElement * Caller
IGUIElement who called the event.
Definition IEventReceiver.h:268
irr::SEvent::SGUIEvent::EventType
gui::EGUI_EVENT_TYPE EventType
Type of GUI Event.
Definition IEventReceiver.h:274
irr::SEvent
SEvents hold information about an event. See irr::IEventReceiver for details on event handling.
Definition IEventReceiver.h:263
irr::SEvent::EventType
EEVENT_TYPE EventType
Definition IEventReceiver.h:414
irr::SEvent::GUIEvent
struct SGUIEvent GUIEvent
Definition IEventReceiver.h:417

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");
}
irr::gui::IGUIWindow
Default moveable window GUI element with border, caption and close icons.
Definition IGUIWindow.h:22
irr::video::SColor
Class representing a 32 bit ARGB color.
Definition SColor.h:202
irr::video::SColor::setAlpha
void setAlpha(u32 a)
Sets the alpha component of the Color.
Definition SColor.h:259
irr::gui::EGUI_DEFAULT_COLOR
EGUI_DEFAULT_COLOR
Enumeration for skin colors.
Definition IGUISkin.h:61
irr::gui::EGDC_COUNT
@ EGDC_COUNT
Definition IGUISkin.h:115

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;
}
irr::IrrlichtDevice::isDriverSupported
static bool isDriverSupported(video::E_DRIVER_TYPE driver)
Check if a driver type is supported by the engine.
Definition IrrlichtDevice.h:277
irr::core::dimension2d::set
dimension2d< T > & set(const T &width, const T &height)
Set to new values.
Definition dimension2d.h:66
irr::createDeviceEx
IRRLICHT_API IrrlichtDevice *IRRCALLCONV createDeviceEx(const SIrrlichtCreationParameters &parameters)
Creates an Irrlicht device with the option to specify advanced parameters.
irr::SIrrlichtCreationParameters
Structure for holding Irrlicht Device creation parameters.
Definition SIrrCreationParameters.h:20
irr::SIrrlichtCreationParameters::WindowSize
core::dimension2d< u32 > WindowSize
Size of the window or the video mode in fullscreen mode. Default: 800x600.
Definition SIrrCreationParameters.h:103
irr::SIrrlichtCreationParameters::DriverType
video::E_DRIVER_TYPE DriverType
Type of video driver used to render graphics.
Definition SIrrCreationParameters.h:100
irr::SIrrlichtCreationParameters::Fullscreen
bool Fullscreen
Should be set to true if the device should run in fullscreen.
Definition SIrrCreationParameters.h:113
- Irrlicht Engine Documentation © 2003-2012 by Nikolaus Gebhardt. Generated on for Irrlicht 3D Engine by Doxygen 1.15.0