main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /** Example 025 Xml Handling
  2. Demonstrates loading and saving of configurations via XML
  3. @author Y.M. Bosman \<yoran.bosman@gmail.com\>
  4. This demo features a fully usable system for configuration handling. The code
  5. can easily be integrated into own apps.
  6. */
  7. #include <irrlicht.h>
  8. #include "exampleHelper.h"
  9. using namespace irr;
  10. using namespace core;
  11. using namespace scene;
  12. using namespace video;
  13. using namespace io;
  14. using namespace gui;
  15. #ifdef _MSC_VER
  16. #pragma comment(lib, "Irrlicht.lib")
  17. #endif
  18. /* SettingManager class.
  19. This class loads and writes the settings and manages the options.
  20. The class makes use of irrMap which is a an associative arrays using a
  21. red-black tree it allows easy mapping of a key to a value, along the way there
  22. is some information on how to use it.
  23. */
  24. class SettingManager
  25. {
  26. public:
  27. // Construct setting managers and set default settings
  28. SettingManager(const stringw& settings_file): SettingsFile(settings_file), NullDevice(0)
  29. {
  30. // Irrlicht null device, we want to load settings before we actually created our device, therefore, nulldevice
  31. NullDevice = irr::createDevice(irr::video::EDT_NULL);
  32. //DriverOptions is an irrlicht map,
  33. //we can insert values in the map in two ways by calling insert(key,value) or by using the [key] operator
  34. //the [] operator overrides values if they already exist
  35. DriverOptions.insert(L"Software", EDT_SOFTWARE);
  36. DriverOptions.insert(L"OpenGL", EDT_OPENGL);
  37. DriverOptions.insert(L"Direct3D9", EDT_DIRECT3D9);
  38. //some resolution options
  39. ResolutionOptions.insert(L"640x480", dimension2du(640,480));
  40. ResolutionOptions.insert(L"800x600", dimension2du(800,600));
  41. ResolutionOptions.insert(L"1024x768", dimension2du(1024,768));
  42. //our preferred defaults
  43. SettingMap.insert(L"driver", L"Direct3D9");
  44. SettingMap.insert(L"resolution", L"640x480");
  45. SettingMap.insert(L"fullscreen", L"0"); //0 is false
  46. }
  47. // Destructor, you could store settings automatically on exit of your
  48. // application if you wanted to in our case we simply drop the
  49. // nulldevice
  50. ~SettingManager()
  51. {
  52. if (NullDevice)
  53. {
  54. NullDevice->closeDevice();
  55. NullDevice->drop();
  56. }
  57. };
  58. /*
  59. Load xml from disk, overwrite default settings
  60. The xml we are trying to load has the following structure
  61. settings nested in sections nested in the root node, like:
  62. \verbatim
  63. <pre>
  64. <?xml version="1.0"?>
  65. <mygame>
  66. <video>
  67. <setting name="driver" value="Direct3D9" />
  68. <setting name="fullscreen" value="0" />
  69. <setting name="resolution" value="1024x768" />
  70. </video>
  71. </mygame>
  72. </pre>
  73. \endverbatim
  74. */
  75. bool load()
  76. {
  77. //if not able to create device don't attempt to load
  78. if (!NullDevice)
  79. return false;
  80. irr::io::IXMLReader* xml = NullDevice->getFileSystem()->createXMLReader(SettingsFile); //create xml reader
  81. if (!xml)
  82. return false;
  83. const stringw settingTag(L"setting"); //we'll be looking for this tag in the xml
  84. stringw currentSection; //keep track of our current section
  85. const stringw videoTag(L"video"); //constant for videotag
  86. //while there is more to read
  87. while (xml->read())
  88. {
  89. //check the node type
  90. switch (xml->getNodeType())
  91. {
  92. //we found a new element
  93. case irr::io::EXN_ELEMENT:
  94. {
  95. //we currently are in the empty or mygame section and find the video tag so we set our current section to video
  96. if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName()))
  97. {
  98. currentSection = videoTag;
  99. }
  100. //we are in the video section and we find a setting to parse
  101. else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() ))
  102. {
  103. //read in the key
  104. stringw key = xml->getAttributeValueSafe(L"name");
  105. //if there actually is a key to set
  106. if (!key.empty())
  107. {
  108. //set the setting in the map to the value,
  109. //the [] operator overrides values if they already exist or inserts a new key value
  110. //pair into the settings map if it was not defined yet
  111. SettingMap[key] = xml->getAttributeValueSafe(L"value");
  112. }
  113. }
  114. //..
  115. // You can add your own sections and tags to read in here
  116. //..
  117. }
  118. break;
  119. //we found the end of an element
  120. case irr::io::EXN_ELEMENT_END:
  121. //we were at the end of the video section so we reset our tag
  122. currentSection=L"";
  123. break;
  124. default:
  125. break;
  126. }
  127. }
  128. // don't forget to delete the xml reader
  129. xml->drop();
  130. return true;
  131. }
  132. // Save the xml to disk. We use the nulldevice.
  133. bool save()
  134. {
  135. //if not able to create device don't attempt to save
  136. if (!NullDevice)
  137. return false;
  138. //create xml writer
  139. irr::io::IXMLWriter* xwriter = NullDevice->getFileSystem()->createXMLWriter( SettingsFile );
  140. if (!xwriter)
  141. return false;
  142. //write out the obligatory xml header. Each xml-file needs to have exactly one of those.
  143. xwriter->writeXMLHeader();
  144. //start element mygame, you replace the label "mygame" with anything you want
  145. xwriter->writeElement(L"mygame");
  146. xwriter->writeLineBreak(); //new line
  147. //start section with video settings
  148. xwriter->writeElement(L"video");
  149. xwriter->writeLineBreak(); //new line
  150. // getIterator gets us a pointer to the first node of the settings map
  151. // every iteration we increase the iterator which gives us the next map node
  152. // until we reach the end we write settings one by one by using the nodes key and value functions
  153. map<stringw, stringw>::Iterator i = SettingMap.getIterator();
  154. for(; !i.atEnd(); i++)
  155. {
  156. //write element as <setting name="key" value="x" />
  157. //the second parameter indicates this is an empty element with no children, just attributes
  158. xwriter->writeElement(L"setting",true, L"name", i->getKey().c_str(), L"value",i->getValue().c_str() );
  159. xwriter->writeLineBreak();
  160. }
  161. xwriter->writeLineBreak();
  162. //close video section
  163. xwriter->writeClosingTag(L"video");
  164. xwriter->writeLineBreak();
  165. //..
  166. // You can add writing sound settings, savegame information etc
  167. //..
  168. //close mygame section
  169. xwriter->writeClosingTag(L"mygame");
  170. //delete xml writer
  171. xwriter->drop();
  172. return true;
  173. }
  174. // Set setting in our manager
  175. void setSetting(const stringw& name, const stringw& value)
  176. {
  177. SettingMap[name]=value;
  178. }
  179. // set setting overload to quickly assign integers to our setting map
  180. void setSetting(const stringw& name, s32 value)
  181. {
  182. SettingMap[name]=stringw(value);
  183. }
  184. // Get setting as string
  185. stringw getSetting(const stringw& key) const
  186. {
  187. //the find function of irr::map returns a pointer to a map::Node
  188. //if the key can be found, otherwise it returns null
  189. //the map node has the function getValue and getKey, as we already know the key, we return node->getValue()
  190. map<stringw, stringw>::Node* n = SettingMap.find(key);
  191. if (n)
  192. return n->getValue();
  193. else
  194. return L"";
  195. }
  196. //
  197. bool getSettingAsBoolean(const stringw& key ) const
  198. {
  199. stringw s = getSetting(key);
  200. if (s.empty())
  201. return false;
  202. return s.equals_ignore_case(L"1");
  203. }
  204. //
  205. s32 getSettingAsInteger(const stringw& key) const
  206. {
  207. //we implicitly cast to string instead of stringw because strtol10 does not accept wide strings
  208. const stringc s = getSetting(key);
  209. if (s.empty())
  210. return 0;
  211. return strtol10(s.c_str());
  212. }
  213. public:
  214. map<stringw, s32> DriverOptions; //available options for driver config
  215. map<stringw, dimension2du> ResolutionOptions; //available options for resolution config
  216. private:
  217. SettingManager(const SettingManager& other); // defined but not implemented
  218. SettingManager& operator=(const SettingManager& other); // defined but not implemented
  219. map<stringw, stringw> SettingMap; //current config
  220. stringw SettingsFile; // filename of the xml
  221. irr::IrrlichtDevice* NullDevice;
  222. };
  223. /*
  224. Application context for global variables
  225. */
  226. struct SAppContext
  227. {
  228. SAppContext()
  229. : Device(0),Gui(0), Driver(0), Settings(0), ShouldQuit(false),
  230. ButtonSave(0), ButtonExit(0), ListboxDriver(0),
  231. ListboxResolution(0), CheckboxFullscreen(0)
  232. {
  233. }
  234. ~SAppContext()
  235. {
  236. if (Settings)
  237. delete Settings;
  238. if (Device)
  239. {
  240. Device->closeDevice();
  241. Device->drop();
  242. }
  243. }
  244. IrrlichtDevice* Device;
  245. IGUIEnvironment* Gui;
  246. IVideoDriver* Driver;
  247. SettingManager* Settings;
  248. bool ShouldQuit;
  249. //settings dialog
  250. IGUIButton* ButtonSave;
  251. IGUIButton* ButtonExit;
  252. IGUIListBox* ListboxDriver;
  253. IGUIListBox* ListboxResolution;
  254. IGUICheckBox* CheckboxFullscreen;
  255. };
  256. /*
  257. A typical event receiver.
  258. */
  259. class MyEventReceiver : public IEventReceiver
  260. {
  261. public:
  262. MyEventReceiver(SAppContext & a) : App(a) { }
  263. virtual bool OnEvent(const SEvent& event)
  264. {
  265. if (event.EventType == EET_GUI_EVENT )
  266. {
  267. switch ( event.GUIEvent.EventType )
  268. {
  269. //handle button click events
  270. case EGET_BUTTON_CLICKED:
  271. {
  272. //Our save button was called so we obtain the settings from our dialog and save them
  273. if ( event.GUIEvent.Caller == App.ButtonSave )
  274. {
  275. //if there is a selection write it
  276. if ( App.ListboxDriver->getSelected() != -1)
  277. App.Settings->setSetting(L"driver", App.ListboxDriver->getListItem(App.ListboxDriver->getSelected()));
  278. //if there is a selection write it
  279. if ( App.ListboxResolution->getSelected() != -1)
  280. App.Settings->setSetting(L"resolution", App.ListboxResolution->getListItem(App.ListboxResolution->getSelected()));
  281. App.Settings->setSetting(L"fullscreen", App.CheckboxFullscreen->isChecked());
  282. if (App.Settings->save())
  283. {
  284. App.Gui->addMessageBox(L"Settings saved",L"Settings saved, please restart for settings to change effect","",true);
  285. }
  286. }
  287. // cancel/exit button clicked, tell the application to exit
  288. else if ( event.GUIEvent.Caller == App.ButtonExit)
  289. {
  290. App.ShouldQuit = true;
  291. }
  292. }
  293. break;
  294. default:
  295. break;
  296. }
  297. }
  298. return false;
  299. }
  300. private:
  301. SAppContext & App;
  302. };
  303. /*
  304. Function to create a video settings dialog
  305. This dialog shows the current settings from the configuration xml and allows them to be changed
  306. */
  307. void createSettingsDialog(SAppContext& app)
  308. {
  309. // first get rid of alpha in gui
  310. for (irr::s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
  311. {
  312. irr::video::SColor col = app.Gui->getSkin()->getColor((irr::gui::EGUI_DEFAULT_COLOR)i);
  313. col.setAlpha(255);
  314. app.Gui->getSkin()->setColor((irr::gui::EGUI_DEFAULT_COLOR)i, col);
  315. }
  316. //create video settings window
  317. gui::IGUIWindow* windowSettings = app.Gui->addWindow(rect<s32>(10,10,400,400),true,L"Videosettings");
  318. app.Gui->addStaticText (L"Select your desired video settings", rect< s32 >(10,20, 200, 40), false, true, windowSettings);
  319. // add listbox for driver choice
  320. app.Gui->addStaticText (L"Driver", rect< s32 >(10,50, 200, 60), false, true, windowSettings);
  321. app.ListboxDriver = app.Gui->addListBox(rect<s32>(10,60,220,120), windowSettings, 1,true);
  322. //add all available options to the driver choice listbox
  323. map<stringw, s32>::Iterator i = app.Settings->DriverOptions.getIterator();
  324. for(; !i.atEnd(); i++)
  325. app.ListboxDriver->addItem(i->getKey().c_str());
  326. //set currently selected driver
  327. app.ListboxDriver->setSelected(app.Settings->getSetting("driver").c_str());
  328. // add listbox for resolution choice
  329. app.Gui->addStaticText (L"Resolution", rect< s32 >(10,130, 200, 140), false, true, windowSettings);
  330. app.ListboxResolution = app.Gui->addListBox(rect<s32>(10,140,220,200), windowSettings, 1,true);
  331. //add all available options to the resolution listbox
  332. map<stringw, dimension2du>::Iterator ri = app.Settings->ResolutionOptions.getIterator();
  333. for(; !ri.atEnd(); ri++)
  334. app.ListboxResolution->addItem(ri->getKey().c_str());
  335. //set currently selected resolution
  336. app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str());
  337. //add checkbox to toggle fullscreen, initially set to loaded setting
  338. app.CheckboxFullscreen = app.Gui->addCheckBox(
  339. app.Settings->getSettingAsBoolean("fullscreen"),
  340. rect<s32>(10,220,220,240), windowSettings, -1,
  341. L"Fullscreen");
  342. //last but not least add save button
  343. app.ButtonSave = app.Gui->addButton(
  344. rect<s32>(80,250,150,270), windowSettings, 2,
  345. L"Save video settings");
  346. //exit/cancel button
  347. app.ButtonExit = app.Gui->addButton(
  348. rect<s32>(160,250,240,270), windowSettings, 2,
  349. L"Cancel and exit");
  350. }
  351. /*
  352. The main function. Creates all objects and does the XML handling.
  353. */
  354. int main()
  355. {
  356. //create new application context
  357. SAppContext app;
  358. //create device creation parameters that can get overwritten by our settings file
  359. SIrrlichtCreationParameters param;
  360. param.DriverType = EDT_SOFTWARE;
  361. param.WindowSize.set(640,480);
  362. // Try to load config.
  363. // I leave it as an exercise for the reader to store the configuration in the local application data folder,
  364. // the only logical place to store config data for games. For all other operating systems I redirect to your manuals
  365. app.Settings = new SettingManager(getExampleMediaPath() + "settings.xml");
  366. if ( !app.Settings->load() )
  367. {
  368. // ...
  369. // Here add your own exception handling, for now we continue because there are defaults set in SettingManager constructor
  370. // ...
  371. }
  372. else
  373. {
  374. //settings xml loaded from disk,
  375. //map driversetting to driver type and test if the setting is valid
  376. //the DriverOptions map contains string representations mapped to to Irrlicht E_DRIVER_TYPE enum
  377. //e.g "direct3d9" will become 4
  378. //see DriverOptions in the settingmanager class for details
  379. map<stringw, s32>::Node* driver = app.Settings->DriverOptions.find( app.Settings->getSetting("driver") );
  380. if (driver)
  381. {
  382. if ( irr::IrrlichtDevice::isDriverSupported( static_cast<E_DRIVER_TYPE>( driver->getValue() )))
  383. {
  384. // selected driver is supported, so we use it.
  385. param.DriverType = static_cast<E_DRIVER_TYPE>( driver->getValue());
  386. }
  387. }
  388. //map resolution setting to dimension in a similar way as demonstrated above
  389. map<stringw, dimension2du>::Node* res = app.Settings->ResolutionOptions.find( app.Settings->getSetting("resolution") );
  390. if (res)
  391. {
  392. param.WindowSize = res->getValue();
  393. }
  394. //get fullscreen setting from config
  395. param.Fullscreen = app.Settings->getSettingAsBoolean("fullscreen");
  396. }
  397. //create the irrlicht device using the settings
  398. app.Device = createDeviceEx(param);
  399. if (app.Device == 0)
  400. {
  401. // You can add your own exception handling on driver failure
  402. exit(0);
  403. }
  404. app.Device->setWindowCaption(L"Xmlhandling - Irrlicht engine tutorial");
  405. app.Driver = app.Device->getVideoDriver();
  406. app.Gui = app.Device->getGUIEnvironment();
  407. createSettingsDialog(app);
  408. //set event receiver so we can respond to gui events
  409. MyEventReceiver receiver(app);
  410. app.Device->setEventReceiver(&receiver);
  411. //enter main loop
  412. while (!app.ShouldQuit && app.Device->run())
  413. {
  414. if (app.Device->isWindowActive())
  415. {
  416. app.Driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, SColor(0,200,200,200));
  417. app.Gui->drawAll();
  418. app.Driver->endScene();
  419. }
  420. app.Device->yield(); // be nice
  421. }
  422. //app destroys device in destructor
  423. return 0;
  424. }
  425. /*
  426. **/