IrrIni - Class for parsing ini files from archive

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

IrrIni - Class for parsing ini files from archive

Post by ArakisTheKitsune »

EDIT: I got few more idea to make this class faster, I will post changes today later or tomorrow evening. And I will also get rid of #include <windows.h> by making my own IsCharAlpha and IsCharAlphaNumeric, thanks pippy3 for giving me idea for that and ideas for some optimization. I will also make class to fill sectionsInfo only when file name is changed instead every time when getKeyValue, getKeyValueInt and getKeyValueBool are called.

EDIT 2: Update complete, have fun :)

EDIT 3: Cache system finished but as it seems it's 8x slower than this but if some one want's to play with it here is code http://www.megaupload.com/?d=KPGVDF4V
I have update code with D_Ini_CommentChar

Hi this is slightly modified version from my game that is currently being developed. This is for anyone that wants to parse ini files from archive.

It's simple and efficient. Of course if anyone have suggestion to make this class better I will gladly hear it.

IrrIni.h

Code: Select all

/*
Note 1: config file can be in this format:

[Section]
key value

There is not need for '=' or any other sign since end of key is
checked for non alphabet and non numeric character.

Note 2: char '[' and ']' should not be used anywhere except for defining
section names in config file.

Note 3: getKeyValue will read whole key row until reach end of row or until finds D_Ini_CommentChar
*/

#pragma once

#include <irrlicht.h>

#pragma comment(lib, "Irrlicht.lib")

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

// default value for getKeyValue that will be returned
#define D_Ini_DefaultValue L""

// default value for getKeyValueBool that will be returned
#define D_Ini_BoolDefaultValue false

// default value for getKeyValueInt that will be returned
#define D_Ini_IntDefaultValue 0

// char that will be used for commenting in config file
#define D_Ini_CommentChar '#'

class CIrrIni
{
public:
    CIrrIni(void);
    ~CIrrIni(void);

    void setIniFileName(IReadFile* file);
    int getKeyValueInt(stringw strKey, stringw strSection);
    void getKeyValue(const stringw strKey, stringw strSection, stringw& result);
    bool getKeyValueBool(stringw strKey, stringw strSection);

private:
    bool isCharAlpha(wchar_t wchar);
    bool isCharAlphaNumeric(wchar_t wchar);

    IReadFile* iniFile;
    int bufferSize;
    stringw buff;

    struct SSectionsInfo
    {
        int numOfSections;
        map<stringw, int> sectionsInfo; // param 1 section name, param 2 section start
    };

    SSectionsInfo sectionsInfo;

};
IrrIni.cpp

Code: Select all

#include "IrrIni.h"

CIrrIni::CIrrIni(void)
{
}

CIrrIni::~CIrrIni(void)
{
}

void CIrrIni::setIniFileName(IReadFile* file)
{
    int start = 0;
    int end = 0;

    iniFile = file;
    bufferSize = iniFile->getSize();
    char* wbuff = new char[bufferSize];
    iniFile->read(wbuff, bufferSize);
    buff = L"";
    buff.append(stringw(wbuff), stringw(wbuff).size());
    delete [] wbuff;

    sectionsInfo.numOfSections = 0;
    sectionsInfo.sectionsInfo.clear();

    start = buff.findFirst('[');

    while(start != -1) // if we have found sections
    {
        end = buff.findNext(']', start);

        if(end != -1)
        {
            sectionsInfo.sectionsInfo[buff.subString(start + 1, end - start - 1)] = end + 1;

            ++sectionsInfo.numOfSections;

            start = buff.findNext('[', end);
        }
    }

}

int CIrrIni::getKeyValueInt(stringw strKey, stringw strSection)
{
    stringw currentKey;
    stringw result = stringw(D_Ini_IntDefaultValue);
    int index = 0;
    int buffPos = 0;

    map<stringw, int>::Node* i = sectionsInfo.sectionsInfo.getRoot();

    while (index != sectionsInfo.sectionsInfo.size())
    {
        buffPos = i->getValue();

        int endOfSection;

        if(sectionsInfo.sectionsInfo.size() > 1)
        {
            endOfSection = buffPos;
        }
        else
        {
            endOfSection = bufferSize;
        }

        if(i->getKey() == strSection) // if we found section
        {
            while(buffPos < endOfSection)
            {
                while(!isCharAlpha(buff[buffPos]))
                {
                    if(buffPos >= endOfSection)
                    {
                        return D_Ini_IntDefaultValue;
                    }

                    ++buffPos;
                }

                while(isCharAlpha(buff[buffPos]) || isCharAlphaNumeric(buff[buffPos]))
                {
                    currentKey += buff[buffPos];
                    ++buffPos;
                }

                if(currentKey == strKey) // if we found key
                {
                    while(!isCharAlphaNumeric(buff[buffPos]))
                    {
                        ++buffPos;
                    }

                    while(isCharAlphaNumeric(buff[buffPos]))
                    {
                        result += buff[buffPos];
                        ++buffPos;
                    }

                    return _wtoi(result.c_str());
                 }
                 else
                 {
                     currentKey = "";
                 }
            }
        }

        ++index;
        ++i;
    }

    return _wtoi(result.c_str());
}

void CIrrIni::getKeyValue(const stringw strKey, stringw strSection, stringw& result)
{
    stringw currentKey;
    result = D_Ini_DefaultValue;
    int index = 0;
    int buffPos = 0;

    map<stringw, int>::Node* i = sectionsInfo.sectionsInfo.getRoot();

    while (index != sectionsInfo.sectionsInfo.size())
    {
        buffPos = i->getValue();

        int endOfSection;

        if(sectionsInfo.sectionsInfo.size() > 1)
        {
            endOfSection = buffPos;
        }
        else
        {
            endOfSection = bufferSize;
        }

        if(i->getKey() == strSection) // if we found section
        {
           while(buffPos < endOfSection)
            {
                while(!isCharAlpha(buff[buffPos]))
                {
                    if(buffPos >= endOfSection)
                    {
                        return;
                    }

                    ++buffPos;
                }

                while(isCharAlpha(buff[buffPos]))
                {
                    currentKey += buff[buffPos];
                    ++buffPos;
                }

                if(currentKey == strKey) // if we found key
                {
                    while(!isCharAlpha(buff[buffPos]))
                    {
                        ++buffPos;
                    }

                    while(buff[buffPos] != 10 && buff[buffPos] != 13 && buff[buffPos] != D_Ini_CommentChar && buff[buffPos] != 65533)
                    {
                        result += buff[buffPos];
                        ++buffPos;
                    }

                    return;
                }
                else
                {
                    currentKey = "";
                }
            }
        }

        ++index;
        ++i;
    }
}

bool CIrrIni::getKeyValueBool(stringw strKey, stringw strSection)
{
    stringw currentKey;
    stringw result = stringw(D_Ini_BoolDefaultValue);
    int index = 0;
    int buffPos = 0;

    map<stringw, int>::Node* i = sectionsInfo.sectionsInfo.getRoot();

    while (index != sectionsInfo.sectionsInfo.size())
    { 
        buffPos = i->getValue();

        int endOfSection;

        if(sectionsInfo.sectionsInfo.size() > 1)
        {
            endOfSection = buffPos;
        }
        else
        {
            endOfSection = bufferSize;
        }

        if(i->getKey() == strSection) // if we found section
        {
            while(buffPos < endOfSection)
            {
                while(!isCharAlpha(buff[buffPos]))
                {
                    if(buffPos >= endOfSection)
                    {
                        return D_Ini_BoolDefaultValue;
                    }

                    ++buffPos;
                }

                while(isCharAlpha(buff[buffPos]))
                {
                    currentKey += buff[buffPos];
                    ++buffPos;
                }

                if(currentKey == strKey) // if we found key
                {
                    while(!isCharAlphaNumeric(buff[buffPos]))
                    {
                        ++buffPos;
                    }

                    while(isCharAlphaNumeric(buff[buffPos]))
                    {
                        result += buff[buffPos];
                        ++buffPos;
                    }

                    return _wtoi(result.c_str());
                }
                else
                {
                    currentKey = "";
                }
            }
        }

        ++index;
        ++i;
    }

    if(result.equals_ignore_case(stringw(L"true")))
    {
        return true;
    }
    else if(result.equals_ignore_case(stringw(L"false")))
    {
        return false;
    }

    return _wtoi(result.c_str());
}

bool CIrrIni::isCharAlpha(wchar_t wchar)
{
    return ((wchar >= 65 && wchar <= 90) || (wchar >= 97 && wchar <= 122));
}

bool CIrrIni::isCharAlphaNumeric(wchar_t wchar)
{
    return (wchar >= 48 && wchar <= 57);
}
Example of usage:

Code: Select all

#include "IrrIni.h"

int _tmain(int argc, _TCHAR* argv[])
{
    stringw archivePath = L"My\path\to\the\archive";
    stringw configFileExtension = L".ini";

    IrrlichtDevice *device = createDevice(video::EDT_NULL);

	if (!device)
    {
		return 1;
    }

    CIrrIni IrrIni;

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

    if(device->getFileSystem()->addFileArchive(archivePath) == false)
    {
        return 1;
    }

    IFileArchive* cardArchive = device->getFileSystem()->getFileArchive(0);
    const IFileList* files = cardArchive->getFileList();

    for(u32 i = 0; i < files->getFileCount(); ++i)
    {
        path file = files->getFileName(i);
        file.make_lower();
        
        // we here check if we got file with extension that we want
        if(file.subString(file.size() - configFileExtension.size(), configFileExtension.size()) == configFileExtension)
        {
            IReadFile* iniFile = cardArchive->createAndOpenFile(i);

            IrrIni.setIniFileName(iniFile);

            stringw iniStringResult;
            IrrIni.getKeyValue(L"Key1", L"Section", iniStringResult);
            int iValue = IrrIni.getKeyValueInt(L"Key2", L"Section");
            bool bValue = IrrIni.getKeyValueBool(L"Key3", L"Section");
            
            iniFile->drop();
        }
    }

    while(device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));

		smgr->drawAll();
		guienv->drawAll();

		driver->endScene();
	}

	device->drop();

	return 0;
}
Last edited by ArakisTheKitsune on Sat Mar 13, 2010 10:24 pm, edited 11 times in total.
Knowledge is power, understanding is wisdom.
B@z
Posts: 876
Joined: Thu Jan 31, 2008 5:05 pm
Location: Hungary

Post by B@z »

looks good, thanks for sharing this
Image
Image
pippy3
Posts: 155
Joined: Tue Dec 15, 2009 7:32 am

Post by pippy3 »

Nice work.

Note there's already an API from Microsoft that phases ini files, so I don't know why you're including <windows.h>. There's a cross compatible solution here
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

Post by ArakisTheKitsune »

Thanks B@z
pippy3 wrote:Nice work.

Note there's already an API from Microsoft that phases ini files
Thanks and I am aware that windows API have GetPrivateProfileString function which don't support parsing config file obtained this way.
pippy3 wrote: so I don't know why you're including <windows.h>. There's a cross compatible solution here
EDIT: Thank for poiting out sollution for IsCharAlpha and IsCharAlphaNumeric functions.
True but as it seems that class that you pointed out reads ini files that are not loaded from archive by Irrlicht engine. In my game there is zip file with textures and config files. When I load file from archive using Irrlich like this:

Code: Select all

device->getFileSystem()->addFileArchive(archivePath);
IFileArchive* cardArchive = device->getFileSystem()->getFileArchive(0);
IReadFile*  iniFile = cardArchive->createAndOpenFile(0); 
Opened file returns IReadFile pointer that is incompatible with your example above. I used <windows.h> because I need to use IsCharAlpha and IsCharAlphaNumeric functions, I know that I could make them but since I am still learning I first want to write to properly work in windows and then maybe later to port it. And thanks for reply I really appreciate that.
Knowledge is power, understanding is wisdom.
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

Post by ArakisTheKitsune »

Fixed few bugs and optimization, enjoy. I believe that is now bug free :mrgreen:
Knowledge is power, understanding is wisdom.
Sylence
Posts: 725
Joined: Sat Mar 03, 2007 9:01 pm
Location: Germany
Contact:

Post by Sylence »

Actually the name IrrIni is already taken ;)
http://irrlicht.sourceforge.net/phpBB2/ ... highlight=

However good work
Software documentation is like sex. If it's good you want more. If it's bad it's better than nothing.
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

Post by ArakisTheKitsune »

Sylence wrote:Actually the name IrrIni is already taken ;)
http://irrlicht.sourceforge.net/phpBB2/ ... highlight=

However good work
Oh sorry I didn't know. I am doing implementation of cache system in this class, I will post changes today or in next 1 or 2 days and I will then change name of class ;)

And thanks :)
Knowledge is power, understanding is wisdom.
Sylence
Posts: 725
Joined: Sat Mar 03, 2007 9:01 pm
Location: Germany
Contact:

Post by Sylence »

Well I don't insist on changing the name. If you wish to keep it I'm ok with it ;)

Just wanted you to know that maybe you could get some questions that are actually related to my code :D
Software documentation is like sex. If it's good you want more. If it's bad it's better than nothing.
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

Post by ArakisTheKitsune »

Sylence wrote:Well I don't insist on changing the name. If you wish to keep it I'm ok with it ;)
Thanks :) if you don't mind I will keep it because I am terrible at making class names :(
Sylence wrote:Just wanted you to know that maybe you could get some questions that are actually related to my code :D
It's nice and simple, it's better than mine xD but one of reason why I made this class is to learn and practice.
Knowledge is power, understanding is wisdom.
CodeGen
Posts: 13
Joined: Tue Apr 21, 2009 5:12 pm

Post by CodeGen »

Hi!

It's a very useful code snippet, thanks for that!

Turning comparisons into table lookups could be faster:

Code: Select all

bool CIrrIni::isCharAlpha(wchar_t wchar) 
{ 
    return alphaCharsTable[char(wchar)];
} 

bool CIrrIni::isCharAlphaNumeric(wchar_t wchar) 
{ 
    return alphaNumericCharsTable[char(wchar)];
}
ArakisTheKitsune
Posts: 73
Joined: Sat Jun 27, 2009 6:52 am

Post by ArakisTheKitsune »

Thanks :)

I think my way is faster, because in my example I don't need to convert wchar_t to char.
Knowledge is power, understanding is wisdom.
Post Reply