Subversion Repositories spk

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "settings.h"
#include "../common/strutils.h"

#include <stdio.h>
#include <windows.h>

static Settings *g_settings;
//---------------------------------------------------------------------------------
#ifdef X2BC_USE_INI_FORMATS 
bool settings_parse_pair_const(const char *pszSectionName, Settings::conststringpair &p)
{
        return g_settings->parseConstant(pszSectionName, p);
}
//---------------------------------------------------------------------------------
bool settings_parse_pair_format(const char *pszSectionName, Settings::conststringpair &p)
{
        return g_settings->parseFormat(pszSectionName, p);
}
//---------------------------------------------------------------------------------
int CompareStatFormats(const void *a, const void *b)
{
        int ida, idb;
        ida=((Settings::StatFormat*)a)->id;
        idb=((Settings::StatFormat*)b)->id;
        
        return ((Settings::StatFormat*)a)->id - ((Settings::StatFormat*)b)->id;
}
#endif // defined(X2BC_USE_INI_FORMATS )
//---------------------------------------------------------------------------------
bool Settings::loadINI(const char *pszFileName)
{
        bool bRes;
        char szSectionName[50];
        
        g_settings=this;
        clearErrors();
        
        GetPrivateProfileString(L"library", L"WriteWarnings", L"off", (LPWSTR)szSectionName, sizeof(szSectionName), (LPCWSTR)pszFileName);
        m_bOutputWarnings=(_stricmp(szSectionName, "on")==0);
        
        GetPrivateProfileString(L"library", L"Convert", L"on", (LPWSTR)szSectionName, sizeof(szSectionName), (LPCWSTR)pszFileName);
        m_bConvert=(_stricmp(szSectionName, "on")==0);
        
        GetPrivateProfileString(L"library", L"FrameWarnings", L"on", (LPWSTR)szSectionName, sizeof(szSectionName), (LPCWSTR)pszFileName);
        m_bStatFormatWarnings=(_stricmp(szSectionName, "on")==0);
        
        GetPrivateProfileString(L"library", L"XtraPointInfo", L"on", (LPWSTR)szSectionName, sizeof(szSectionName), (LPCWSTR)pszFileName);
        m_bXtraPntInfo=(_stricmp(szSectionName, "on")==0);
        
        GetPrivateProfileString(L"library", L"XtraX3BobInfo", L"off", (LPWSTR)szSectionName, sizeof(szSectionName), (LPCWSTR)pszFileName);
        m_bXtraX3BobInfo=(_stricmp(szSectionName, "on")==0);
        
#ifdef X2BC_USE_INI_FORMATS
        size_t size;
        
        GetPrivateProfileString("library", "Constants", "Const", szSectionName, sizeof(szSectionName), pszFileName);
        
        char *pszConstants=readSection(szSectionName, pszFileName, &size);
        if(size==0){
                error(Error, E_SectionEmpty, "Section with constants '%s' not defined or empty.", szSectionName);
                bRes=false;
        }
        else
                bRes=parseLines(szSectionName, pszConstants, size, settings_parse_pair_const);
                
        delete[] pszConstants;
        
        if(bRes){
                GetPrivateProfileString("library", "Default", "Format", szSectionName, sizeof(szSectionName), pszFileName);
                char *pszFormat=readSection(szSectionName, pszFileName, &size);
                if(size==0){
                        error(Error, E_SectionEmpty, "Section with frame formats '%s' not defined or empty.", szSectionName);
                        bRes=false;
                }
                else
                        bRes=parseLines(szSectionName, pszFormat, size, settings_parse_pair_format);

                delete[] pszFormat;
        }
#else
        bRes=true;
#endif // !defined(X2BC_USE_INI_FORMATS)
        return bRes;
}
//---------------------------------------------------------------------------------
void Settings::error(ErrorType type, ErrorCodes code, const char *format, ...)
{
        va_list ap;
        va_start(ap, format);
        
        ErrorMsg *e=new ErrorMsg();
        e->type=type;
        e->code=code;
        
        _vsnprintf(e->message, sizeof(e->message), format, ap);
        va_end(ap);
        
        errors.push_back(e);
}
//---------------------------------------------------------------------------------

#ifdef X2BC_USE_INI_FORMATS
char * Settings::readSection(const char *pszSection, const char *pszFileName, size_t *size)
{
        char *data;
        int _size=0, res;
        
        do{
                _size+=1500;
                data=new char[_size];
                if(data==NULL) break;
                res=GetPrivateProfileSection(pszSection, data, _size, pszFileName);
                if(res==_size - 2){
                        delete[] data;
                }
        }
        while(res==_size - 2);
        if(size) *size=res;
        return data;
}
//---------------------------------------------------------------------------------
bool Settings::parseLines(const char *pszSectionName, char *pszData, size_t size, ParsePairCallback *callback)
{
        size_t count;
        char **lines=lineexplode(pszData, size, &count);
        bool bRes=true;
        
        for(size_t i=0; i < count; i++){
                char *line=lines[i];
        
                int j=0;
                bool bComment=false;
                char *equal, *left, *right;
                
                left=right=equal=0;
                
                while(line[j]!=0){
                        switch(line[j]){
                                case '=':
                                        line[j]=0;
                                        equal=line + j;
                                        left=line;
                                        break;
                                case ';':
                                        line[j]=0;
                                        j--; // so the main while(...) will encouter this added 0 and terminate
                                        break;
                        }
                        j++;
                }
                if(equal){
                        right=equal + 1;
                
                        conststringpair p=parsePair(left, right);
                        if(callback(pszSectionName, p)==false) {
                                bRes=false;
                                break; // break if parsing fails
                        }
                }
                else
                        error(Warning, W_LineIgnored, "Section \"%s\": line '%s' ignored.", pszSectionName, line);
        }
        
        delete[] lines;
        return bRes;
}
//---------------------------------------------------------------------------------
// trim white spaces from left and right
Settings::conststringpair Settings::parsePair(const char *left, const char *right)
{
        conststringpair p;
        p.left=trim((char*)left);
        p.right=trim((char*)right);
        return p;
}
//---------------------------------------------------------------------------------
bool Settings::parseConstant(const char *pszSectionName, Settings::conststringpair &p)
{
        bool res=true;
        NumberFormat *f=new NumberFormat();
        const char *pszMultiplier=0, *pszFormat=0;
        char *left, *right;
        
        // backup the original strings for error reporting
        size_t len=strlen(p.left);
        left=new char[len + 1];
        memcpy(left, p.left, len + 1);
        len=strlen(p.right);
        right=new char[len + 1];
        memcpy(right, p.right, len + 1);
        
        // the left operand (constant name)
        if(*p.left==0){
                error(ErrorType::Error, E_BadConstantDeclaration, "Section \"%s\": line: '%s = %s':\nMissing constant name.", pszSectionName, left, right);
                res=false;
        }
        else if(strlen(p.left) > 1){
                error(ErrorType::Error, E_BadConstantDeclaration, "Section \"%s\": line: '%s = %s':\nConstant name must be exactly 1 character.", pszSectionName, left, right);
                res=false;
        }
        else if(!((*p.left | 0x20) > 'a') && ((*p.left | 0x20) < 'z')){
                error(Error, E_BadConstantDeclaration, "Section \"%s\": Constant \"%c\": constant name must be a upper or lowercase character (A - Z).", pszSectionName, *p.left);
                res=false;
        }
        if(res==false){
                delete[] left; delete[] right;
                return res;
        }
        f->type=*p.left;
        
        // the right operand (constant multiplier and format)
        pszMultiplier=p.right;
        char *pos=strchr(p.right, ',');
        if(pos){
                *pos=0;
                pszFormat=trim(++pos);
        }       
        if(pszMultiplier==0 || *pszMultiplier==0){
                error(ErrorType::Error, E_BadConstantDeclaration, "Section \"%s\": line: '%s = %s':\nMissing constant multiplier.", pszSectionName, left, right);
                res=false;
        }
        else if(pszFormat==0 || *pszFormat==0){ 
                error(ErrorType::Error, E_BadConstantDeclaration, "Section \"%s\": line: '%s = %s':\nMissing output format specifier.", pszSectionName, left, right);
                res=false;
        }
        else{
                f->multiplier=atof(pszMultiplier);
                if(f->multiplier==0){
                        error(ErrorType::Warning, W_ConstantAlwaysZero, "Section \"%s\": line: '%s = %s':\nMultiplier is 0. Result will be always zero. Is this the intent?", pszSectionName, left, right);
                }
        }
        if(res==true){
                if(stricmp(pszFormat, "integer")==0)
                        f->outformat=NumberFormat::Integer;
                else if(stricmp(pszFormat, "float")==0)
                        f->outformat=NumberFormat::Float;
                else{
                        error(ErrorType::Error, E_BadConstantDeclaration, "Section \"%s\": line: '%s = %s':\nUnknown output format '%s'.", pszSectionName, left, right, pszFormat);
                        res=false;
                }
        }
        delete[] left;
        delete[] right;
        
        if(res)
                m_constants.push_back(f);
        
        return res;
}
//---------------------------------------------------------------------------------
bool Settings::parseFormat(const char *pszSectionName, conststringpair &p)
{
        bool bRes;
        switch(p.left[0]){
                case 0:
                        error(ErrorType::Error, E_BadFormatDeclaration, "Section \"%s\": invalid line format: '%s = %s':\nID expected.", pszSectionName, p.left, p.right);
                        bRes=false;
                        break;
                case '$':
                        bRes=parseStatFormat(pszSectionName, p);
                        break;
                case 'N': // don't read the N constants - we use algorithm instead if INI
                        bRes=true;
                        break;
                default:
                        error(Warning, W_LineIgnored, "Section \"%s\": ignoring line '%s = %s':\nUnknown ID type.", pszSectionName, p.left, p.right);
                        bRes=true;
                
        }
        return bRes;
}
//---------------------------------------------------------------------------------
bool Settings::parseStatFormat(const char *pszSectionName, conststringpair &p)
{
        char *left, *right;
        bool bRes=true;
        // backup the original strings for error reporting
        size_t len=strlen(p.left);
        left=new char[len + 1];
        memcpy(left, p.left, len + 1);
        len=strlen(p.right);
        right=new char[len + 1];
        memcpy(right, p.right, len + 1);
        
        StatFormat *f=new StatFormat();
        
        if(p.left[0]==0){
                error(ErrorType::Error, E_BadFormatDeclaration, "Section \"%s\": line '%s = %s':\nMissing ID.", pszSectionName, left, right);
                bRes=false;
        }
        else{
                if(p.left[0]=='$') p.left++;
                f->id=hextoi(p.left);
        }
        if(bRes){
                char *ch=(char*)p.right;
                if(*ch!='"'){
                        error(ErrorType::Error, E_BadFormatDeclaration, "Section \"%s\": line '%s = %s':\nExpected begining of string after \"%s =\".", pszSectionName, left, right, left);
                        bRes=false;
                }
                else{
                        ch++;
                        while(*ch!=0 && *ch!='"') ch++;
                        if(*ch==0){
                                error(ErrorType::Error, E_BadFormatDeclaration, "Section \"%s\": line '%s = %s':\nExpected end of string after \"%s\".", pszSectionName, left, right, right);
                                bRes=false;
                        }
                        else{
                                *ch=0;
                                bRes=parseFormatString(pszSectionName, f, (char*)p.right + 1);
                                if(bRes) formats.push_back(f);
                        }
                }
        }
        
        return bRes;
}
//---------------------------------------------------------------------------------
bool isnum(char c)
{
        return (c >= '0' && c <= '9');
}
//---------------------------------------------------------------------------------
bool Settings::parseFormatString(const char *pszSectionName, Settings::StatFormat *f, char *format)
{
        bool bRes=true;
        
        char *ch, *start;
        start=format;
        
        // if format begins with - a warning will be issued if such format is used in bob file
        if(*start!=0 && *start=='-') {
                start++;
                f->issueWarning=true;
        }
        // skip any spaces after the '-'
        while(*start==' ') start++;
        
        while(*start!=0){
                ch=start;
                if(isnum(*ch)==false){
                        error(Error, E_BadFormatDeclaration, "Section \"%s\": line id $%x:\nMissing number at start of '%s'.", pszSectionName, f->id, format);
                        return false;
                }
                else{
                        while(isnum(*ch)) ch++;
                        if(*ch==0){
                                error(Error, E_BadFormatDeclaration, "Section \"%s\": line id $%x:\nMissing constant after '%s'.", pszSectionName, f->id, ch);
                                return false;
                        }
                        else{
                                StatFormat::token *t=new StatFormat::token();
                                char old=*ch;
                                *ch=0;
                                t->count=atoi(start);
                                t->type=old;
                                *ch=old;
                                
                                t->numFormat=findNumberFormat(t->type);
                                if(t->numFormat==0){
                                        error(Error, E_BadFormatDeclaration, "Section \"%s\": line id $%x:\nConstant \"%c\" not defined in '%s'.", pszSectionName, f->id, t->type, format);
                                        delete t;
                                        return false;
                                }
                                f->tokens.push_back(t);
                                // increase the cumulative count so it's easy to tell how many numbers is needed 
                                // to match the format
                                f->cumulativeTokenCount+=t->count; 
                                
                                start=ch+1;
                                while(*ch!=0 && isnum(*ch)==false) ch++;
                                old=*ch;
                                *ch=0;
                                size_t size=ch-start;
                                if(size){
                                        t->data=new char[size + 1];
                                        memcpy(t->data, start, size + 1);
                                }
                                *ch=old;
                                start=ch;
                        }
                }
        }
        return bRes;
}
//---------------------------------------------------------------------------------
Settings::NumberFormat * Settings::findNumberFormat(char ch)
{
        char c;
        for(NumberFormatList::iterator &it=m_constants.begin(); it!=m_constants.end(); ++it){
                c=it->type;
                if(c==ch) return *it;
        }
        return NULL;
}
//---------------------------------------------------------------------------------
#endif // defined(X2BC_USE_INI_FORMATS)