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>
#include "exampleHelper.h"
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#ifdef _MSC_VER
#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:
    
    SettingManager(const stringw& settings_file): SettingsFile(settings_file), NullDevice(0)
    {
        
        NullDevice = irr::createDevice(irr::video::EDT_NULL);
        
        
        
        DriverOptions.insert(L"Software", EDT_SOFTWARE);
        DriverOptions.insert(L"OpenGL", EDT_OPENGL);
        DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9);
        
        ResolutionOptions.insert(L"640x480", dimension2du(640,480));
        ResolutionOptions.insert(L"800x600", dimension2du(800,600));
        ResolutionOptions.insert(L"1024x768", dimension2du(1024,768));
        
        SettingMap.insert(L"driver", L"Direct3D9");
        SettingMap.insert(L"resolution", L"640x480");
        SettingMap.insert(L"fullscreen", L"0"); 
    }
    
    
    
    ~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: 
    <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 (!NullDevice)
            return false;
        irr::io::IXMLReader* xml = NullDevice->getFileSystem()->createXMLReader(SettingsFile);  
        if (!xml)
            return false;
        const stringw settingTag(L"setting"); 
        stringw currentSection; 
        const stringw videoTag(L"video"); 
        
        while (xml->read())
        {
            
            switch (xml->getNodeType())
            {
                
                case irr::io::EXN_ELEMENT:
                {
                    
                    if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName()))
                    {
                        currentSection = videoTag;
                    }
                    
                    else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() ))
                    {
                        
                        stringw key = xml->getAttributeValueSafe(L"name");
                        
                        if (!key.empty())
                        {
                            
                            
                            
                            SettingMap[key] = xml->getAttributeValueSafe(L"value");
                        }
                    }
                    
                    
                    
                }
                break;
                
                case irr::io::EXN_ELEMENT_END:
                    
                    currentSection=L"";
                break;
                default:
                    break;
            }
        }
        
        xml->drop();
        return true;
    }
    
    bool save()
    {
        
        if (!NullDevice)
            return false;
        
        irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter( SettingsFile );
        if (!xwriter)
            return false;
        
        xwriter->writeXMLHeader();
        
        xwriter->writeElement(L"mygame");
        xwriter->writeLineBreak();                  
        
        xwriter->writeElement(L"video");
        xwriter->writeLineBreak();                  
        
        
        
        map<stringw, stringw>::Iterator i = SettingMap.getIterator();
        for(; !i.atEnd(); i++)
        {
            
            
            xwriter->writeElement(L"setting",true, L"name", i->getKey().c_str(), L"value",i->getValue().c_str() );
            xwriter->writeLineBreak();
        }
        xwriter->writeLineBreak();
        
        xwriter->writeClosingTag(L"video");
        xwriter->writeLineBreak();
        
        
        
        
        xwriter->writeClosingTag(L"mygame");
        
        xwriter->drop();
        return true;
    }
    
    void setSetting(const stringw& name, const stringw& value)
    {
        SettingMap[name]=value;
    }
    
    void setSetting(const stringw& name, s32 value)
    {
        SettingMap[name]=stringw(value);
    }
    
    stringw getSetting(const stringw& key) const
    {
        
        
        
        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
    {
        
        const stringc s = getSetting(key);
        if (s.empty())
            return 0;
        return strtol10(s.c_str());
    }
public:
    map<stringw, s32> DriverOptions; 
    map<stringw, dimension2du> ResolutionOptions; 
private:
    SettingManager(const SettingManager& other); 
    SettingManager& operator=(const SettingManager& other); 
    map<stringw, stringw> SettingMap; 
    stringw SettingsFile; 
    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;
    
    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 )
            {
                
                case EGET_BUTTON_CLICKED:
                {
                    
                    if ( event.GUIEvent.Caller == App.ButtonSave )
                    {
                        
                        if ( App.ListboxDriver->getSelected() != -1)
                            App.Settings->setSetting(L"driver", App.ListboxDriver->getListItem(App.ListboxDriver->getSelected()));
                        
                        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);
                        }
                    }
                    
                    else if ( event.GUIEvent.Caller == App.ButtonExit)
                    {
                        App.ShouldQuit = true;
                    }
                }
                break;
                default:
                    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)
{
    
    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);
    }
    
    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);
    
    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);
    
    map<stringw, s32>::Iterator i = app.Settings->DriverOptions.getIterator();
    for(; !i.atEnd(); i++)
        app.ListboxDriver->addItem(i->getKey().c_str());
    
    app.ListboxDriver->setSelected(app.Settings->getSetting("driver").c_str());
    
    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);
    
    map<stringw, dimension2du>::Iterator ri = app.Settings->ResolutionOptions.getIterator();
    for(; !ri.atEnd(); ri++)
        app.ListboxResolution->addItem(ri->getKey().c_str());
    
    app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str());
    
    app.CheckboxFullscreen = app.Gui->addCheckBox(
            app.Settings->getSettingAsBoolean("fullscreen"),
            rect<s32>(10,220,220,240), windowSettings, -1,
            L"Fullscreen");
    
    app.ButtonSave = app.Gui->addButton(
            rect<s32>(80,250,150,270), windowSettings, 2,
            L"Save video settings");
    
    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()
{
    
    SAppContext app;
    
    SIrrlichtCreationParameters param;
    param.DriverType = EDT_SOFTWARE;
    param.WindowSize.set(640,480);
    
    
    
    app.Settings = new SettingManager(getExampleMediaPath() + "settings.xml");
    if ( !app.Settings->load() )
    {
        
        
        
    }
    else
    {
        
        
        
        
        
        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() )))
            {
                
                param.DriverType = static_cast<E_DRIVER_TYPE>( driver->getValue());
            }
        }
        
        map<stringw, dimension2du>::Node* res = app.Settings->ResolutionOptions.find( app.Settings->getSetting("resolution") );
        if (res)
        {
            param.WindowSize = res->getValue();
        }
        
        param.Fullscreen = app.Settings->getSettingAsBoolean("fullscreen");
    }
    
    app.Device = createDeviceEx(param);
    if (app.Device == 0)
    {
        
        exit(0);
    }
    app.Device->setWindowCaption(L"Xmlhandling - Irrlicht engine tutorial");
    app.Driver  = app.Device->getVideoDriver();
    app.Gui     = app.Device->getGUIEnvironment();
    createSettingsDialog(app);
    
    MyEventReceiver receiver(app);
    app.Device->setEventReceiver(&receiver);
    
    while (!app.ShouldQuit && app.Device->run())
    {
        if (app.Device->isWindowActive())
        {
            app.Driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, SColor(0,200,200,200));
            app.Gui->drawAll();
            app.Driver->endScene();
        }
        app.Device->sleep(10);
    }
    
    return 0;
}