Tutorial 13: Loading and saving simple XML files

XML files are used as an easy way to read and store data which is useful for storing configuration settings from applications. It’s always good practice to allow your application to be customised by the user, be it by turning off the Wiimote rumble, enabling background music, disabling prompts, etc. In this tutorial we’ll learn how to load and save simple settings by using XML files. You can grab the PDF of this tutorial here: codemii-tutorial-13

Understanding XML files

If you don’t know what an XML file looks like here is an example:

[sourcecode language=’c’]

Homebrew Browser
teknecal
0.3.1
200905010000
Browse homebrew apps
[/sourcecode]

As you can tell, it’s well structured and easily readable. You firstly have the root element which tells the XML parser that this is an XML document. This isn’t much importance to us but note that all valid XML files must have this root element.
[sourcecode language=’c’] [/sourcecode]

Next we have “app” as an element with an attribute of “version” and the attribute value of “1”. Elements are defined as being the text after the <. The attribute is the text that is inside the element and is equal to something, in this case it’s version = 1.
[sourcecode language=’c’] [/sourcecode]

Moving on, we have an element beneath the “app” element.
[sourcecode language=’c’]Homebrew Browser[/sourcecode]

This means that this element belongs to the “app” element. In this case the element is “name” and this time the element has a content of “Homebrew Browser”. We need to close off this element by using the / and the elements name.

The next few elements we can skip as they require the exact same processing as above. We then reach the end of our app element.
[sourcecode language=’c’][/sourcecode]

You can either end the file or keep adding more elements at the end such as:
[sourcecode language=’c’]
Homebrew Browser 2
teknecal2

[/sourcecode]

You will firstly need to download libxml which can be found here: http://wiichat.googlecode.com/files/mxml-wii.tgz

Extract and then copy mxml-wii\mxml\lib\libmxml.a to C:\devkitPro\libogc\lib\wii and mxml-wii\mxml\lib\include\mxml.h to C:\devkitPro\libogc\include. Thanks to Beardface for porting this XML library.

You’ll need to add –lxmxl to LIBS: in your makefile and also add #include in your main.c file. Also make sure you have #include and the other necessary fat initialisation functions.

Here is our source code for both saving and loading XML files: tutorial13.zip

Saving XML files

So now we have a very basic understanding of how XML files are structured and can now proceed to save our own XML files. We’ll be saving our data as attributes.

Going on the simple settings theme, let’s assume we only have either a 0 or a 1 to store in our XML file and that we have 3 variables which are setting_background_music, setting_rumble and setting_tips. We have our function below which saves our settings which I’ll explain below.

[sourcecode language=’c’]void update_settings() {
mxml_node_t *xml;
mxml_node_t *data;
xml = mxmlNewXML(“1.0”);

data = mxmlNewElement(xml, “settings”);

char set1[1];
sprintf(set1, “%i”, setting_background_music);
mxmlElementSetAttr(data, “setting_background_music”, set1);
char set2[1];
sprintf(set2, “%i”, setting_rumble);
mxmlElementSetAttr(data, “setting_rumble”, set2);
char set3[1];
sprintf(set3, “%i”, setting_tips);
mxmlElementSetAttr(data, “setting_tips”, set3);

FILE *f;
f = fopen(“sd:/settings.xml”, “wb”);

if (f == NULL) {
fclose(f);
printf(“Settings could not be written.\n”);
}
else {
mxmlSaveFile(xml, f, MXML_NO_CALLBACK);
fclose(f);
mxmlDelete(data);
mxmlDelete(xml);
printf(“Settings Saved\n”);
}
}[/sourcecode]

The first three items are just standard XML things which we do. We will be creating a XML file in memory at this point in time.
[sourcecode language=’c’]mxml_node_t *xml;
mxml_node_t *data;
xml = mxmlNewXML(“1.0”);[/sourcecode]

We now create a new element with the name settings which would look like in the file.
[sourcecode language=’c’]data = mxmlNewElement(xml, “settings”);[/sourcecode]

Then we start adding our attributes, but before we do so, these attributes are of type strings and our 0 or 1 (integer) needs to be changed into a string, so we use sprintf. We create an empty char array with a length of 1. We then sprintf to set1 the value of setting_background_music.
[sourcecode language=’c’]char set1[1];
sprintf(set1, “%i”, setting_background_music); [/sourcecode]

Now our char array has the value of either “0” or “1”. We can then set an attribute in our element settings to show this. We’ll call our attribute the same name as our variable which is setting_background_music for simplicity. Our setting element would like: (assuming we had the variable set to 1).
[sourcecode language=’c’]mxmlElementSetAttr(data, “setting_background_music”, set1); [/sourcecode]

Now we just repeat this to the rest of our variables.
[sourcecode language=’c’]char set2[1];
sprintf(set2, “%i”, setting_rumble);
mxmlElementSetAttr(data, “setting_rumble”, set2);
char set3[1];
sprintf(set3, “%i”, setting_tips);
mxmlElementSetAttr(data, “setting_tips”, set3); [/sourcecode]

Ok, so now we are done we can begin writing the file. As normal we open the file for writing, check if we can write to the file, etc.
[sourcecode language=’c’]FILE *f;
f = fopen(“sd:/settings.xml”, “wb”);

if (f == NULL) {
fclose(f);
printf(“File could not be written to\n”);
}[/sourcecode]

Now we can save our XML to file with just one line. After we have saved the file we close our file and then delete the XML in memory.
[sourcecode language=’c’]else {
mxmlSaveFile(xml, f, MXML_NO_CALLBACK);
fclose(f);
mxmlDelete(data);
mxmlDelete(xml);
printf(“XML Saved\n”);
}[/sourcecode]

Loading XML files

So now we know what we’ve saved in the XML file and what it will look like this:

We can parse our XML file and find out what our settings for each variable are; we can use the below code to do this.

[sourcecode language=’c’]void load_settings() {
mxml_node_t *tree;
mxml_node_t *data;

FILE *fp = fopen(“sd:/settings.xml”, “rb”);
if (fp == NULL) {
fclose(fp);
}
else {
fseek (fp , 0, SEEK_END);
long settings_size = ftell (fp);
rewind (fp);

if (settings_size > 0) {

tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp);

data = mxmlFindElement(tree, tree, “settings”, NULL, NULL, MXML_DESCEND);

if (mxmlElementGetAttr(data,”setting_background_music”)) {
setting_background_music = atoi(mxmlElementGetAttr(data,”setting_background_music”));
printf(“Setting for background music loaded\n”);
}
if (mxmlElementGetAttr(data,”setting_rumble”)) {
setting_rumble = atoi(mxmlElementGetAttr(data,”setting_rumble”));
printf(“Setting for rumble loaded\n”);
}
if (mxmlElementGetAttr(data,”setting_tips”)) {
setting_tips = atoi(mxmlElementGetAttr(data,”setting_tips”));
printf(“Setting for tips loaded\n”);
}

mxmlDelete(data);
mxmlDelete(tree);
printf(“Settings loaded.\n”);
}
else {
fclose(fp);
unlink(“sd:/settings.xml”);
}
}
}[/sourcecode]

We start almost the same as before when saving our XML file, we initialise variables to store the XML and then we just use our standard fopen to read our XML file.

[sourcecode language=’c’]void load_settings() {
mxml_node_t *tree;
mxml_node_t *data;

FILE *fp = fopen(“sd:/settings.xml”, “rb”);
if (fp == NULL) {
fclose(fp);
}[/sourcecode]

It’s a good idea to check that this file has content otherwise we’ll have some errors, so we can do so using ftell to tell us the file size, if it’s over 0 bytes, the file has some content.

[sourcecode language=’c’] else {
fseek (fp , 0, SEEK_END);
long settings_size = ftell (fp);
rewind (fp);

if (settings_size > 0) {[/sourcecode]

Next we’ll load our file to our XML variable which we created at the start and then close the file, read all our “settings” elements and store them in the variable data.

[sourcecode language=’c’] tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp);

data = mxmlFindElement(tree, tree, “settings”, NULL, NULL, MXML_DESCEND); [/sourcecode]

Now we start reading our settings one by one. We make reference to our data variable which contains all the settings and say that we only want to read the element value for the element called “setting_check_size”.

It’s important to have this check, otherwise if our settings XML file didn’t contain the element we were trying to read, it would cause some issues. If this element is present, it’s stored as a string so we use atoi to convert it to an int and store it in our variable “setting_check_size”.

[sourcecode language=’c’] if (mxmlElementGetAttr(data,”setting_check_size”)) {
setting_check_size = atoi(mxmlElementGetAttr(data,”setting_check_size”));
}[/sourcecode]

We do the same for the rest of our settings.

[sourcecode language=’c’] if (mxmlElementGetAttr(data,” setting_rumble “)) {
setting_rumble = atoi(mxmlElementGetAttr(data,”setting_rumble”));
}
if (mxmlElementGetAttr(data,”setting_tips”)) {
setting_tips = atoi(mxmlElementGetAttr(data,”setting_tips”));
}[/sourcecode]

We can remove the XML file from memory and we are done.

[sourcecode language=’c’] mxmlDelete(data);
mxmlDelete(tree);
printf(“Settings loaded.\n”);
}
else {
fclose(fp);
}
}
}[/sourcecode]

We’ve now successfully read all our settings from the XML we saved and that wraps up this tutorial. You can now save and load your applications simple settings using XML files.

30 Responses to “Tutorial 13: Loading and saving simple XML files”

  1. pembo says:

    Thanks – this will be extremely useful!

  2. Nicholas Anthony Roge says:

    Yet another well written guide, Teknecal.

  3. John says:

    Nice tutorial again! Could you please add the PDF version? Nicer to print and easier to organize. Thanks in advance.

  4. Michele says:

    Sorry if I post on this tutorial, but since i’ve started 2 days ago programming i’ve started from the first tutorial…but it’s quite old and the post are old too…so i’ll ask my question here:

    I’ve problems with tutorial 4 and 5 (6 and so on not tested yet),I get (on the hbc) the message that the executable i’ve built is not a valid wii application.

    Any idea? (I’ve tryed with the sd method and using wiiload,same result).
    Many thanks!

  5. pembo says:

    Think I spotted an error


    You’ll need to add –lxmxl to LIBS: in your makefile and also add #include in your main.c file. Also make sure you have #include and the other necessary fat initialisation functions.

    I guess this should be -lmxml

  6. pembo says:

    Thank you once again for this tutorial Teknecal – it really helped me to get going with the mxml lib. I’m using this to ‘internationalise’ BootMii Config editor.

    It’s worth taking a look at the official documentation for this.
    http://www.minixml.org/mxml.html if people have more questions.

    Even with this documentation, I still haven’t figured out how to get a text value out of an element rather than an attribute, but for now, I’ve put everything into attributes to make it easier, and I’ll try and figure this out some other time.

  7. pececito says:

    hi
    im spanish sorry for my english

    thanks for tutorials, very nice work.
    i have a dude, can i edit a boot.dol ?
    can i open the .dol of other programs to see the code in c ? im think is very useful to learn to program.

    please if you know a form do a tutorial or send me a email thanks

  8. pececito says:

    sorry other question :
    are you doing a pdf ? i wanna it because im printing all pdf to do a book 😛
    thanks a lot
    and thanks pembo for your bootmii config editor because i changed the options in a file in pc but no change in wii lol, but whit your program i can see in colour the bootmii in pal =)

  9. Fall of Socrates says:

    It’s been a while, so I’m not sure if you’re still making them, but if you are, would it be possible to write a tutorial on how to display video?

  10. sctech-tr says:

    hi! looks like libxml link is dead now, as stated in the wiibrew page. if you still have the file, can you archive it?

Leave a Reply