Currently it is only explained here how to set up MiniINI using GCC on Unix. It should be reasonably simple to set it up in any other environment, though. All you need is to add library path to where you have libminiini.a and/or libminiini-dbg.a (bin/ by default), link with it, and add header path to top MiniINI directory.
First, install MiniINI (see instructions in readme). Create a file named makefile in your project directory, with the following contents:
debug: example.cpp g++ -g -Wall -o example-debug example.cpp -lminiini-dbg optimized: example.cpp g++ -O3 -o example-optimized example.cpp -lminiini clean: rm -f example.o example-debug example |
When you type make, this makefile will compile debug build by default, linked with debug build of MiniINI. To build optimized build (linked with optimized MiniINI, type make optimized. To clean up built files, type make clean.
In the debug target, -g is used to add debugging symbols to the exe and -Wall to provide basic warnings. Note -lminiini-dbg, which links the program with debug build of MiniINI.
In the optimized target, we use -O3 for maximum optimization. (Note: -Os can sometimes give better results with MiniINI, especially if your program is small.) -lminiini links to the default (optimized) build of MiniINI.
The clean target removes any files that might result from compilation.
There is only one header you need to include to use MiniINI. That would be miniini.h. We also include iostream so we can show what we read from the inifile.
#include<miniini.h> |
For this example, create a file named example.ini in your project directory, with these contents:
[general] AppName=HugeRTS2 AppVersion=2.4beta [unittypes] 1=bad_guy 2=good_guy [bad_guy] Hitpoints=100 Speed=24.5 ;Km/s Weapon=BFG Evil=true [good_guy] Hitpoints=75 Speed=2.45 ;Km/s Weapon=AK47 Evil=no |
This is the kind of ini file that could be used in a game. We have some basic settings, then a list of units and a section for every one of them.
In this example, all code is in the main() function.
int main() { |
The most important class you’re going to use in MiniINI is INIFile. This class handles loading of ini files and accesing ini sections. Here we declare an INIFile, then open our example file using the OpenFile method. This method will load given ini file to memory and process it. If succesful, it returns true. If the file does not exist, could not be opened, is corrupted or is otherwise invalid, it returns false and we print out an error to standard output.
INIFile ini; if(!ini.OpenFile("example.ini")) std::cout << "ERROR: Could not open example.ini"; |
We get access to the [general] section using the GetSection method. This method returns a pointer to the INISection we want, which is a class we use to read values from a section. If the section does not exist, it returns NULL and we print out an error.
Note that MiniINI is case sensitive, so, for instance, [general] and [General] is not the same thing.
INISection * general = ini.GetSection("general"); if(!general) std::cout << "ERROR: Missing section [general] in example.ini"; |
Now we can load some data from [general]. First we declare variables to which we then read data from the section using the ReadString and ReadInt methods. Other methods for reading values from tags are ReadFloat and ReadBool.
All these methods take two arguments: name of the tag and reference to variable to store read data in. Name of the tag must be a valid zero terminated string, otherwise a call to ReadXXX will result in undefined behavior. In case of ReadString, only a pointer to string inside the section is returned, so the data is not copied. This requires the char pointer to be const, so that it can’t change internal INISection data.
NOTE: This means that you have to copy the string data somewhere before INIFile is destroyed if you still want to use it (i.e. before it goes out of scope or is deleted).
These methods return true on success and false if the tag does not exist or is invalid (for instance when we ReadInt from a tag that does not contain data parsable as an int - this also issues a warning when using the debug build.)
When we have the data, we print it out to standard output so we can see what we’ve read.
const char * appname; int appbuild; if(!general->ReadString("AppName", appname)) std::cout << "ERROR: Missing tag AppName= in section [general] in example.ini"; if(!general->ReadInt("AppBuild", appbuild)) std::cout << "ERROR: Missing tag AppBuild= in section [general] in example.ini"; std::cout << appname << " build " << appbuild; |
Now you should have the basic idea about how to use MiniINI. Here we will explain how to use array reading functions. Reading arrays is a little bit more difficult. Look at the [unittypes] section in example.ini : You can see tags 1= and 2= . These are array indices. (in ini files they start from 1). For array loading to work, indices must be in the correct order.
In order to load such data into an array, we use ReadXXXs (note the s) methods, which require base tag name, reference to buffer to store loaded data, and its capacity.
Base tag name is prefix for all tags in the array. E.g. if base tag name was "weapon", we’d be reading from tags "weapon1=", "weapon2=", etc. To read from our tags, we use empty string "" as prefix so tags are indices without prefix.. Before reading, we have to allocate a buffer with some capacity, and then pass that buffer along with its capacity to ReadXXXs. ReadXXXs will read at most as many elements as the given capacity. ReadXXXs will then return the number of array elements actually read.
Now to get to the code: Let’s say [unittypes] is an array storing ini section names of types of units in a game. First we get access to the [unittypes] section using GetSection. Then we allocate a buffer to read array data to. Similarly to ReadString, ReadStrings needs a buffer of const char* which will then point directly to data inside INISection. Finally, we read the data and store the number of elements in unitcount for later use. We then check if it’s nonzero. If it’s not, we print out an error.
INISection * unittypes = ini.GetSection("unittypes"); if(!unittypes) std::cout << "ERROR: Missing section [unittypes] in example.ini"; unsigned cap = 64; const char * * unitbuf = new const char * [cap]; unsigned unitcount = unittypes->ReadStrings("", unitbuf, cap); if(!unitcount) std::cout << "ERROR: Could not read int array from section [unittypes] in example.ini"; |
Now we can read data of the units section names of which we store in our array.
So, we iterate over unitcount of elements in unitbuf and use each element as a name of section to get. Then we read Hitpoints=, Speed=, Weapon= and Evil= tags as int, float, string and bool, respectively. Again note that MiniINI is case sensitive, so Evil= is not same as evil=.
Also note that bools can have various values for true and false:
1, yes, true are parsed as true
0, no, false are parsed as false
NOTE: that e.g. True, TRUE are not parsed as true, and will result in
MiniINI not reading the value and, when using debug build, print a warning.
Finally we print out unit data to see what we’ve read.
for(unsigned unit = 0; unit < unitcount; ++unit) { INISection * unitsection = ini.GetSection(unitbuf[unit]); if(!unitsection) std::cout << "ERROR: Missing section [" << unitbuf[unit] << "] in example.ini"; int hitpoints; float speed; const char * weapon; bool evil; if(!unitsection->ReadInt("Hitpoints", hitpoints)) std::cout << "ERROR: Missing or invalid tag Hitpoints= in section [" << unitbuf[unit] << "] in example.ini"; if(!unitsection->ReadFloat("Speed", speed)) std::cout << "ERROR: Missing or invalid tag Speed= in section [" << unitbuf[unit] << "] in example.ini"; if(!unitsection->ReadString("Weapon", weapon)) std::cout << "ERROR: Missing or invalid tag Weapon= in section [" << unitbuf[unit] << "] in example.ini"; if(!unitsection->ReadBool("Evil", evil)) std::cout << "ERROR: Missing or invalid tag Evil= in section [" << unitbuf[unit] << "] in example.ini"; std::cout << "Unit " << unitbuf[unit] << " data:\n"; std::cout << " Hitpoints: " << hitpoints << " Speed: " << speed << " Weapon: " << weapon << " Evil: " << evil << "\n"; } |
Now all that is left is to finish the main function. This is where our ini file goes out of scope, and all its data is deleted.
return 0; } |
Last updated 2010-01-04 15:35:15 CEST