/*
* Copyright (c) 2003-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Reader class.
*
*/

#include "SDCGlobals.h"
#include "SDCReader.h"
#include "SDCIIDConstants.h"
#include "SDCException.h"
#include "SDCCompat.h"

// Make std namespace available for compatibility
namespace std {}
using namespace std;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSDCReader::CSDCReader( CSDCData* aData )
    {
    iData = aData;
    }

CSDCReader::~CSDCReader()
    {
    }

//////////////////////////////////////////////////////////////////////
// Other methods
//////////////////////////////////////////////////////////////////////

void CSDCReader::Parse()
    {
    gInput.NextTokenAllowLF();
    if( sd_wcscasecmp( gInput.iToken, L"SKINTYPE" ) != 0 ) UnexpectedTokenError( gInput.iToken, L"SKINTYPE" );
    ParseSkintype();

    gInput.NextTokenAllowLF();
    if( sd_wcscasecmp( gInput.iToken, L"UID" ) == 0 )
        {
        ParseUID();
        gInput.NextTokenAllowLF();
        }
    else if( sd_wcscasecmp( gInput.iToken, L"PID" ) == 0 )
        {
        ParsePID();
        gInput.NextTokenAllowLF();
        }
    else
        {
        GeneratePID();
        }

    if( sd_wcscasecmp( gInput.iToken, L"NAME" ) != 0 ) UnexpectedTokenError( gInput.iToken, L"NAME" );
    ParseName();

    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"NAME" ) == 0 )
            {
            ParseName();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"LANGUAGEOVERRIDE" ) == 0 )
            {
            ParseLanguageOverride();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"LANGUAGEPARENT" ) == 0 )
            {
            ParseParentUID();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"PROTECT" ) == 0 )
            {
            ParseProtect();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"AUTHOR" ) == 0 )
            {
            ParseAuthor();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"COPYRIGHT" ) == 0 )
            {
            ParseCopyright();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"TARGETDEVICE" ) == 0 )
            {
            ParseTargetDevice();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"TOOL" ) == 0 )
            {
            ParseTool();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"BITMAP" ) == 0 )
            {
            ParseBitmap();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"COLORTABLE" ) == 0 )
            {
            ParseColorTable();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"FRAME" ) == 0 )
            {
            ParseFrame();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"BMPANIM" ) == 0 )
            {
            ParseBmpAnim();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"APPICON" ) == 0 )
            {
            ParseAppIcon();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"PALETTE" ) == 0 )
            {
            ParsePalette();
            }
        else if( (sd_wcscasecmp( gInput.iToken, L"SOUND" ) == 0)
            || (sd_wcscasecmp( gInput.iToken, L"STRING" ) == 0) )
            {
            ParseString();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"SCALABLEITEM" ) == 0 )
            {
            if( !iData->IsScalable() ) throw CSDCException( ESDCContentError, "SCALABLEITEM can only be included in scalable skins" );
            ParseScalableItem();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"ANIMATION" ) == 0 )
            {
            if( !iData->IsScalable() ) throw CSDCException( ESDCContentError, "ANIMATION can only be included in scalable skins" );
            ParseAnimation();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"BITMAPPATH" ) == 0 )
            {
            ParseBitmapPath();
            }
        else if( sd_wcscasecmp( gInput.iToken, L"RESTRICTION" ) == 0 )
            {
            ParseRestriction();
            }
        else
            {
            wprintf(L"DEBUG: Unknown token: \"%s\"\n", gInput.iToken );
            throw CSDCException( ESDCParseError, "Unknown token encountered or syntax error" );
            }
        }
    }

void CSDCReader::ParseSkintype()
    {
    gInput.NextToken();

    if( sd_wcscasecmp( gInput.iToken, L"normal" ) == 0 )
        {
        iData->iSkinType = 0x0;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"system" ) == 0 )
        {
        iData->iSkinType = 0x1;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"scalable" ) == 0 )
        {
#if !defined( RD_ENHANCED_SKINNING )
        throw CSDCException( ESDCNotSupportedError, "Scalable skins not supported" );
#endif
        iData->iSkinType = 0x80;
        return; // No other types are supported, if scalable
        }
    else
        {
        throw CSDCException( ESDCParseError, "Unknown or missing parameter for SKINTYPE" );
        }

    if( gInput.NextToken() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"language=AH" ) == 0 )
            {
            iData->iSkinType |= 0x102;
            }
        else
            {
            throw CSDCException( ESDCParseError, "Unknown or missing additional parameter for SKINTYPE" );
            }
        }
    }

void CSDCReader::ParseUID()
    {
    gInput.NextToken();
    iData->iPid.iPID2 = 0; // Timestamp
    iData->iPid.iPID1 = gInput.ConvertToNumber( gInput.iToken ); // UID
    }

void CSDCReader::ParsePID()
    {
    gInput.NextToken();
    iData->iPid.iPID2 = gInput.ConvertToNumber( gInput.iToken ); // Timestamp

    if( iData->iPid.iPID2 == 0 )
        {
        throw CSDCException( ESDCParseError, "PID can not start with zero" );
        }

    gInput.NextToken();
    iData->iPid.iPID1 = gInput.ConvertToNumber( gInput.iToken ); // Random number
    }

void CSDCReader::GeneratePID()
    {
    iData->iPid.iPID2 = 0;
    iData->iPid.iPID1 = 0;
    }

void CSDCReader::ParseName()
    {
    gInput.NextToken();
    int languageID = gInput.ConvertToNumber( gInput.iToken );

    gInput.NextStringToken();
    iData->AppendNameEntry( languageID, gInput.iToken );
    }

void CSDCReader::ParseLanguageOverride()
    {
    if( iData->IsScalable() )
        throw CSDCException( ESDCParseError, "Language override can not be used with scalable skins" );

    gInput.NextToken();
    if( sd_wcscasecmp( gInput.iToken, L"AH" ) != 0 )
        {
        throw CSDCException( ESDCParseError, "Unknown or missing parameter for LANGUAGEOVERRIDE" );
        }

    gInput.NextToken();
    int value = gInput.ConvertToNumber( gInput.iToken );
    if( gInput.NextToken() )
        {
        iData->iAHOverridePid.iPID1 = gInput.ConvertToNumber( gInput.iToken );
        iData->iAHOverridePid.iPID2 = value;
        }
    else
        {
        iData->iAHOverridePid.iPID2 = 0;
        iData->iAHOverridePid.iPID1 = value;
        }
    }

void CSDCReader::ParseParentUID()
    {
    if( iData->IsScalable() )
        throw CSDCException( ESDCParseError, "Parent UID can not be used with scalable skins" );

    gInput.NextToken();
    int value = gInput.ConvertToNumber( gInput.iToken );
    if( gInput.NextToken() )
        {
        iData->iParentPid.iPID1 = gInput.ConvertToNumber( gInput.iToken );
        iData->iParentPid.iPID2 = value;
        }
    else
        {
        iData->iParentPid.iPID2 = 0;
        iData->iParentPid.iPID1 = value;
        }
    }

void CSDCReader::ParseProtect()
    {
    gInput.NextToken();
    if( sd_wcscasecmp( gInput.iToken, L"disablecopy" ) != 0 )
        {
        throw CSDCException( ESDCParseError, "Unknown or missing parameter for PROTECT" );
        }

    iData->iProtection = 1;
    }

void CSDCReader::ParseAuthor()
    {
    gInput.NextStringToken();
    wcscpy( iData->iAuthor, gInput.iToken );
    }

void CSDCReader::ParseCopyright()
    {
    gInput.NextStringToken();
    wcscpy( iData->iCopyright, gInput.iToken );
    }

void CSDCReader::ParseTargetDevice()
    {
    gInput.NextStringToken();
    iData->AppendTargetDeviceEntry( gInput.iToken );
    }

void CSDCReader::ParseTool()
    {
    gInput.NextStringToken();
    wcscpy( iData->iTool, gInput.iToken );
    }

void CSDCReader::ParseBitmap()
    {
    gInput.NextToken();

    bool appIcon = false;
    if( sd_wcscasecmp( gInput.iToken, L"CLASS=appicon" ) == 0 )
        {
        appIcon = true;
        gInput.NextToken();
        }

    int restriction( iData->iCurrentRestriction );
    const TSDCIIDEntry* iid = NULL;
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );

        gInput.NextToken();
        }

    TSDCColorDepth colorDepth = gInput.ConvertToColorDepth( gInput.iToken );

    gInput.NextToken();
    if( !iid )
        {
        iid = SDCIIDConstants::FindBmpName( gInput.iToken );
        if( !iid ) UnknownIIDError( gInput.iToken );
        }

    bool svg = CSDCInput::IsSvgFile( gInput.iToken );
    TSDCMBMEntry* mbmEntry = iData->AppendMbmEntry( svg, colorDepth, gInput.iToken );
    int mbmIndex = mbmEntry->iIndex;

    TSDCImageAttributes attributes;
    attributes.iAttributeFlags = ESDCImageAttributeNone;
    attributes.iCoordX = 0;
    attributes.iCoordY = 0;
    attributes.iSizeW = 0;
    attributes.iSizeH = 0;
    attributes.iAlignmentFlags = ESDCImageAlignNone;

    if( iData->IsScalable() ) {
        attributes.iAttributeFlags |= ESDCImageAttributeNBC;
        }

    int maskIndex = -1;

    while( gInput.NextToken() )
        {
        if( sd_wcsncasecmp( gInput.iToken, L"MASK=", 5 ) == 0 )
            {
            if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
            if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                {
                printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                }
            else
                {
                const wchar_t* maskFile = gInput.iToken+5;
                mbmEntry->iMaskColorDepth = ESDCColorDepth1;
                wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                }
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcsncasecmp( gInput.iToken, L"SOFTMASK=", 9 ) == 0 )
            {
            if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
            if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                {
                printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                }
            else
                {
                const wchar_t* maskFile = gInput.iToken+9;
                mbmEntry->iMaskColorDepth = ESDCColorDepth8;
                wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                }
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MASK" ) == 0 )
            {
            if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
            mbmEntry->iMaskColorDepth = ESDCColorDepth1;
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"SOFTMASK" ) == 0 )
            {
            if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
            mbmEntry->iMaskColorDepth = ESDCColorDepth8;
            maskIndex = mbmIndex+1;
            }
        else
            {
            ProcessAttribute( attributes, gInput.iToken );
            }
        }

    TSDCIID realIid;
    realIid.iMajor = iid->iMajor;
    realIid.iMinor = iid->iMinor;
    iData->CreateBitmapDef( realIid, mbmIndex, maskIndex, attributes, appIcon, restriction );
    }

void CSDCReader::ParseColorTable()
    {
    gInput.NextToken();

    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for COLORTABLE" );
        }

    vector<TSDCColorTableEntry> colors;

    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0 )
            {
            iData->CreateColorTableDef( realIid, colors, restriction );
            return;
            }
        else if( sd_wcsncasecmp( gInput.iToken, L"IDX=", 4 ) == 0 )
            {
            TSDCColorTableEntry entry;
            entry.iIndex = gInput.ConvertToNumber( gInput.iToken+4 );
            entry.iRgb = 0;
            colors.push_back( entry );
            }
        else if( sd_wcsncasecmp( gInput.iToken, L"RGB=", 4 ) == 0 )
            {
            TSDCColorTableEntry entry;
            entry.iIndex = -1;
            int rgb = gInput.ConvertToNumber( gInput.iToken+4 );
            // SymbianOS actually uses BGR order
            entry.iRgb =
                ((rgb&0xff)<<16) | (rgb&0xff00) | ((rgb&0xff0000)>>16);
            colors.push_back( entry );
            }
        else throw CSDCException( ESDCParseError, "Unknown token inside COLORTABLE" );
        }

    throw CSDCException( ESDCParseError, "No END token for COLORTABLE" );
    }

void CSDCReader::ParseFrame()
    {
    gInput.NextToken();

    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for FRAME" );
        }

    vector<TSDCIID> elements;

    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
            {
            iData->CreateFrameDef( realIid, elements, restriction );
            return;
            }
        else
            {
            elements.push_back( ParseBitmapSource( false, restriction ) );
            }
        }

    throw CSDCException( ESDCParseError, "No END token for FRAME" );
    }

void CSDCReader::ParseBmpAnim()
    {
    gInput.NextToken();

    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for BMPANIM" );
        }

    int interval = -1;
    int playMode = 0;
    int flash = 0;
    bool svganim = false;

    while( gInput.NextToken() )
        {
        if( sd_wcsncasecmp( gInput.iToken, L"INTERVAL=", 9 ) == 0 )
            {
            interval = gInput.ConvertToNumber( gInput.iToken+9 );
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MODE=play" ) == 0 )
            {
            playMode = 0;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MODE=cycle" ) == 0 )
            {
            playMode = 1;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MODE=bounce" ) == 0 )
            {
            playMode = 2;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MODE=svganim" ) == 0 )
            {
            playMode = 255;
            svganim = true;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"FLASH" ) == 0 )
            {
            flash = 1;
            }
        else
            {
            UnexpectedTokenError( gInput.iToken, L"<BMPANIM parameter>" );
            }
        }

    vector<TSDCBmpAnimFrame> frames;

    int time = -1;
    int posx = 0;
    int posy = 0;
    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcsncasecmp( gInput.iToken, L"TIME=", 5 ) == 0 )
            {
            time = gInput.ConvertToNumber( gInput.iToken+5 );
            }
        else if( sd_wcsncasecmp( gInput.iToken, L"POS=", 4 ) == 0 )
            {
            wchar_t buf[512];
            wcscpy( buf, gInput.iToken+4 );
            wchar_t* p = wcsstr( buf, L"," );
            if( !p ) throw CSDCException( ESDCParseError, "No comma found in POS parameter of BMPANIM" );
            *p = 0;
            posx = gInput.ConvertToNumber( buf );
            posy = gInput.ConvertToNumber( p+1 );
            }
        else if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0 )
            {
            iData->CreateBmpAnimDef( realIid, interval, playMode, flash, frames, restriction );
            return;
            }
        else
            {
            TSDCIID frameIid = ParseBitmapSource( false, restriction, svganim );

            TSDCBmpAnimFrame entry;
            entry.iIID = frameIid;
            entry.iTime = time;
            entry.iPosX = posx;
            entry.iPosY = posy;
            frames.push_back( entry );
            time = -1;
            posx = 0;
            posy = 0;
            }
        }

    throw CSDCException( ESDCParseError, "No END token for BMPANIM" );
    }

void CSDCReader::ParseAppIcon()
    {
    gInput.NextToken();
    int uid;
    if( sd_wcsncasecmp( gInput.iToken, L"UID=", 4 ) == 0 )
        {
        uid = gInput.ConvertToNumber( gInput.iToken+4 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "UID parameter expected for APPICON" );
        }

    int restriction( iData->iCurrentRestriction );

    vector<TSDCIID> iconBitmaps;
    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
            {
            TSDCIID realIid;
            realIid.iMajor = EAknsMajorAppIcon;
            realIid.iMinor = uid;
            iData->CreateAppIconDef( realIid, iconBitmaps, restriction );
            return;
            }
        else
            {
            TSDCIID iconIid = ParseBitmapSource( true, restriction );
            iconBitmaps.push_back( iconIid );
            TSDCBitmapDef* bmpDef = iData->GetBitmapDef( iconIid );
            if( !bmpDef )
                {
                printf( "WARNING: Application icon possibly uses a bitmap outside its class (line %i)\n", gInput.iLineNumber );
                }
            else if( bmpDef->iAppIconBitmap == false )
                {
                printf( "WARNING: Application icon uses a bitmap outside its class (line %i)\n", gInput.iLineNumber );
                }
            }
        }

    throw CSDCException( ESDCParseError, "No END token for APPICON" );
    }

void CSDCReader::ParsePalette()
    {
    gInput.NextToken();
    if( sd_wcsncasecmp( gInput.iToken, L"SCHEME=", 7 ) == 0 )
        {
        iData->iPalettePid.iPID2 = 0;
        iData->iPalettePid.iPID1 = gInput.ConvertToNumber( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "SCHEME parameter expected for PALETTE" );
        }
    }

void CSDCReader::ParseString()
    {
    gInput.NextToken();
    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for SOUND/STRING" );
        }

    gInput.NextStringToken();
    iData->CreateStringDef( realIid, gInput.iToken, restriction );
    }

void CSDCReader::ParseScalableItem()
    {
    gInput.NextToken();
    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for SCALABLEITEM" );
        }

    gInput.NextToken();
    int input = 0;
    if( sd_wcsncasecmp( gInput.iToken, L"INPUT=", 6 ) == 0 )
        {
        input = CSDCInput::ConvertToLayer( gInput.iToken+6 );
        }
    else if( sd_wcsncasecmp( gInput.iToken, L"REFIID=", 7 ) == 0 )
        {
        // Reference only
        TSDCIID refIid;
        int ignored;
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+7 );
        StripIIDPrefix( iidBuf, ignored );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+7 );
        refIid.iMajor = iid->iMajor;
        refIid.iMinor = iid->iMinor;
        char buf[512];
        CSDCInput::ConvertToAscii( buf, gInput.iToken );
        if( !iData->IsDefined( refIid ) )
            {
            printf( "INFO: IID reference (%s) currently points to non-existent element (line %i)\n", buf, gInput.iLineNumber );
            }
        iData->CreateScalableItemRefDef( realIid, refIid, restriction );
        return;
        }
    else
        {
        throw CSDCException( ESDCParseError, "INPUT parameter expected for SCALABLEITEM" );
        }

    gInput.NextToken();
    int output = 0;
    if( sd_wcsncasecmp( gInput.iToken, L"OUTPUT=", 7 ) == 0 )
        {
        output = CSDCInput::ConvertToLayer( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "OUTPUT parameter expected for SCALABLEITEM" );
        }

    vector<TSDCEffectCommand> commands;
    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
            {
            iData->CreateScalableItemDef( realIid, input, output, commands, restriction );
            return;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"EFFECT" ) == 0)
            {
            TSDCEffectCommand command;
            ParseEffectCommand( command );
            commands.push_back( command );
            }
        else
            {
            UnexpectedTokenError( gInput.iToken, L"EFFECT or END" );
            }
        }

    throw CSDCException( ESDCParseError, "No END token for SCALABLEITEM" );
    }

void CSDCReader::ParseAnimation()
    {
    gInput.NextToken();
    TSDCIID realIid;
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        }
    else
        {
        throw CSDCException( ESDCParseError, "IID parameter expected for ANIMATION" );
        }

    gInput.NextToken();
    int input = 0;
    if( sd_wcsncasecmp( gInput.iToken, L"INPUT=", 6 ) == 0 )
        {
        input = CSDCInput::ConvertToLayer( gInput.iToken+6 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "INPUT parameter expected for ANIMATION" );
        }

    gInput.NextToken();
    int output = 0;
    if( sd_wcsncasecmp( gInput.iToken, L"OUTPUT=", 7 ) == 0 )
        {
        output = CSDCInput::ConvertToLayer( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "OUTPUT parameter expected for ANIMATION" );
        }

    gInput.NextToken();
    int mininterval = 0;
    if( sd_wcsncasecmp( gInput.iToken, L"MININTERVAL=", 12 ) == 0 )
        {
        mininterval = CSDCInput::ConvertToNumber( gInput.iToken+12 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "MININTERVAL parameter expected for ANIMATION" );
        }

    gInput.NextTokenAllowLF();

    bool morphing = false;
    if( sd_wcscasecmp( gInput.iToken, L"MORPHING" ) == 0 )
        {
        morphing = true;

        gInput.NextTokenAllowLF();
        }

    vector<TSDCEffectCommand> preprocessCommands;
    if( sd_wcscasecmp( gInput.iToken, L"PREPROCESS" ) == 0 )
        {
        bool terminated( false );
        while( gInput.NextTokenAllowLF() )
            {
            if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
                {
                terminated = true;
                break;
                }
            else if( sd_wcscasecmp( gInput.iToken, L"EFFECT" ) == 0)
                {
                TSDCEffectCommand command;
                ParseEffectCommand( command );
                preprocessCommands.push_back( command );
                }
            else
                {
                UnexpectedTokenError( gInput.iToken, L"EFFECT or END" );
                }
            }
        if( !terminated ) throw CSDCException( ESDCParseError, "No END token for ANIMATION/PREPROCESS" );

        gInput.NextTokenAllowLF();
        }

    vector<TSDCEffectCommand> animCommands;
    vector<TSDCAnimParamGroup> values;
    vector<TSDCAnimParamGroup> timingModels;
    vector<TSDCAnimParamGroup> sizeBoundParams;

    bool terminated( false );
    do
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
            {
            terminated = true;
            break;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"COMMAND" ) == 0)
            {
            TSDCEffectCommand command;
            ParseEffectCommand( command );
            animCommands.push_back( command );
            }
        else if( sd_wcscasecmp( gInput.iToken, L"VALUE" ) == 0)
            {
            gInput.NextToken();
            if( sd_wcsncasecmp( gInput.iToken, L"UID=", 4 ) )
                throw new CSDCException( ESDCParseError, "UID parameter expected for VALUE" );
            int uid = CSDCInput::ConvertToNumber( gInput.iToken+4 );

            gInput.NextToken();
            if( sd_wcsncasecmp( gInput.iToken, L"TIMINGID=", 9 ) )
                throw new CSDCException( ESDCParseError, "TIMINGID parameter expected for VALUE" );
            int timingId = CSDCInput::ConvertToNumber( gInput.iToken+9 );

            TSDCAnimParamGroup paramGroup;

            while( gInput.NextTokenAllowLF() && sd_wcscasecmp( gInput.iToken, L"END" ) )
                {
                TSDCEffectParameter parameter;
                ParseEffectParameter( parameter );
                paramGroup.iParameters.push_back( parameter );
                }

            paramGroup.iValueA = uid;
            paramGroup.iValueB = timingId;
            values.push_back( paramGroup );
            }
        else if( sd_wcscasecmp( gInput.iToken, L"TIMINGMODEL" ) == 0)
            {
            gInput.NextToken();
            if( sd_wcsncasecmp( gInput.iToken, L"UID=", 4 ) )
                throw new CSDCException( ESDCParseError, "UID parameter expected for TIMINGMODEL" );
            int uid = CSDCInput::ConvertToNumber( gInput.iToken+4 );

            TSDCAnimParamGroup paramGroup;

            while( gInput.NextTokenAllowLF() && sd_wcscasecmp( gInput.iToken, L"END" ) )
                {
                TSDCEffectParameter parameter;
                ParseEffectParameter( parameter );
                paramGroup.iParameters.push_back( parameter );
                }

            paramGroup.iValueA = uid;
            paramGroup.iValueB = 0;
            timingModels.push_back( paramGroup );
            }
        else if( sd_wcscasecmp( gInput.iToken, L"SIZEBOUNDPARAM" ) == 0)
            {
            gInput.NextToken();
            TSDCEffectParameter parameter;
            parameter.iType = 0;
            wcscpy( parameter.iName, gInput.iToken );
            parameter.iNumber = 0;

            gInput.NextToken();
            if( sd_wcsncasecmp( gInput.iToken, L"VALUEID=", 8 ) )
                throw new CSDCException( ESDCParseError, "VALUEID parameter expected for SIZEBOUNDPARAM" );
            int valueId = CSDCInput::ConvertToNumber( gInput.iToken+8 );

            gInput.NextToken();
            int flags = 0;
            if( sd_wcscasecmp( gInput.iToken, L"FLAGS=W" ) == 0 )
                {
                flags = 1;
                }
            else if( sd_wcscasecmp( gInput.iToken, L"FLAGS=H" ) == 0 )
                {
                flags = 2;
                }
            else if( sd_wcscasecmp( gInput.iToken, L"FLAGS=W_AND_H" ) == 0 )
                {
                flags = 3;
                }
            else throw new CSDCException( ESDCParseError, "FLAGS=W|H|W_AND_H parameter expected for SIZEBOUNDPARAM" );

            TSDCAnimParamGroup paramGroup;
            paramGroup.iValueA = valueId;
            paramGroup.iValueB = flags;
            paramGroup.iParameters.push_back( parameter );
            sizeBoundParams.push_back( paramGroup );
            }
        else
            {
            UnexpectedTokenError( gInput.iToken, L"COMMAND, VALUE, TIMINGMODEL, SIZEBOUNDPARAM, or END" );
            }
        }
    while( gInput.NextTokenAllowLF() );
    if( !terminated ) throw CSDCException( ESDCParseError, "No END token for ANIMATION" );

    iData->CreateAnimationDef( realIid, input, output, mininterval, preprocessCommands, animCommands,
        values, timingModels, sizeBoundParams, morphing, restriction );
    }

void CSDCReader::ParseBitmapPath()
    {
    if( gInput.NextToken() )
        {
        bool changed( true );
        if( sd_wcscasecmp( gInput.iToken, iData->iBmpPath ) == 0 ) changed = false;
        iData->SetBmpPath( gInput.iToken );
        char buf[512];
        CSDCInput::ConvertToAscii( buf, gInput.iToken );
        if( changed )
            printf("NOTE: Bitmap source path set to %s\n", buf );
        }
    else
        {
        iData->SetBmpPath( NULL );
        printf("NOTE: Bitmap source path set to automatic.\n" );
        }
    }

void CSDCReader::ParseRestriction()
    {
    if( !iData->IsScalable() )
        printf( "WARNING: RESTRICTION elements is only allowed in scalable skins, layout types will not work with this skin (line %i)\n", gInput.iLineNumber );

    gInput.NextToken();
    if( sd_wcscasecmp( gInput.iToken, L"S60_2_6" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0206;
        printf("NOTE: Current restriction set to 2.6\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_2_7" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0207;
        printf("NOTE: Current restriction set to 2.7\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_2_8" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0208;
        printf("NOTE: Current restriction set to 2.8\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_3_0" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0300;
        printf("NOTE: Current restriction set to 3.0\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_3_1" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0301;
        printf("NOTE: Current restriction set to 3.1\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_3_2" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0302;
        printf("NOTE: Current restriction set to 3.2\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"S60_5_0" ) == 0 )
        {
        iData->iCurrentRestriction = 0x0500;
        printf("NOTE: Current restriction set to 5.0\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"LAY_W" ) == 0 )
        {
        iData->iCurrentRestriction = 0x00010000;
        printf("NOTE: Current restriction set to Layout/Non-Mirrored\n");
        }
    else if( sd_wcscasecmp( gInput.iToken, L"LAY_AH" ) == 0 )
        {
        iData->iCurrentRestriction = 0x00020000;
        printf("NOTE: Current restriction set to Layout/Mirrored\n");
        }
    else if( sd_wcsncasecmp( gInput.iToken, L"LANG=", 5 ) == 0 )
        {
        int language = (CSDCInput::ConvertToNumber( gInput.iToken+5 ))&0xffff;
        printf("NOTE: Current restriction set to Language/langcode:%d\n", language);
        iData->iCurrentRestriction = language|0x00030000;
        iData->iLanguageVector.push_back(language|0x00030000);
        }
    else if( sd_wcscasecmp( gInput.iToken, L"NONE" ) == 0 )
        {
        iData->iCurrentRestriction = 0;
        printf("NOTE: Current restriction cleared\n");
        }
    else
        {
        throw CSDCException( ESDCParseError, "Unknown specifier for RESTRICTION element" );
        }
    }

TSDCIID CSDCReader::ParseBitmapSource( const bool aAppIcon, const int aRestriction, const bool aSvgAnim )
    {
    int restriction( iData->iCurrentRestriction );
    if( sd_wcsncasecmp( gInput.iToken, L"IID=", 4 ) == 0 )
        {
        wchar_t iidBuf[512];
        wcscpy( iidBuf, gInput.iToken+4 );
        StripIIDPrefix( iidBuf, restriction );
        char buf[512];
        gInput.ConvertToAscii( buf, gInput.iToken );
        if( restriction && (restriction == aRestriction) )
            {
            printf( "NOTE: IID reference (%s) is restricted, but shares the restrictions of the compound. Relative validity can not be checked (line %i)\n", buf, gInput.iLineNumber );
            }
        else if( restriction )
            {
            printf( "WARNING: IID reference (%s) is restricted. Relative validity can not be checked and may be broken (line %i)\n", buf, gInput.iLineNumber );
            }
        else if( aRestriction )
            {
            printf( "NOTE: Non-restricted IID reference (%s) inside restricted compound (line %i)\n", buf, gInput.iLineNumber );
            }
        const TSDCIIDEntry* iid = SDCIIDConstants::FindPlain( iidBuf );
        if( !iid ) UnknownIIDError( gInput.iToken+4 );
        TSDCIID realIid;
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        return realIid;
        }
    restriction = aRestriction;

    TSDCColorDepth colorDepth = CSDCInput::ConvertToColorDepth( gInput.iToken );

    gInput.NextToken();
    TSDCIID realIid;
    const TSDCIIDEntry* iid = SDCIIDConstants::FindBmpName( gInput.iToken );
    if( iid )
        {
        realIid.iMajor = iid->iMajor;
        realIid.iMinor = iid->iMinor;
        if( restriction )
            {
            printf( "NOTE: Known IID (0x%x,0x%x) referenced inside restricted compound. Relative validity can not be checked (line %i)\n", realIid.iMajor, realIid.iMinor, gInput.iLineNumber );
            }
        }
    else
        {
        if( (iData->iPid.iPID2==0) && (iData->iPid.iPID1!=0) )
            {
            realIid.iMajor = iData->iPid.iPID1;
            }
        else
            {
            realIid.iMajor = -4;
            }
        realIid.iMinor = iData->iNextOwnMinor++;
        char buf[512];
        gInput.ConvertToAscii( buf, gInput.iToken );
        printf( "NOTE: Generated IID (0x%x,0x%x) for %s (line %i)\n", realIid.iMajor, realIid.iMinor, buf, gInput.iLineNumber );
        if( restriction )
            {
            printf( "NOTE: Restriction inherited to generated IID (0x%x,0x%x) for %s (line %i)\n", realIid.iMajor, realIid.iMinor, buf, gInput.iLineNumber );
            }
        }

    bool svg = CSDCInput::IsSvgFile( gInput.iToken );
    TSDCMBMEntry* mbmEntry = iData->AppendMbmEntry( svg, colorDepth, gInput.iToken, aSvgAnim );
    int mbmIndex = mbmEntry->iIndex;

    TSDCImageAttributes attributes;
    attributes.iAttributeFlags = ESDCImageAttributeNone;
    attributes.iCoordX = 0;
    attributes.iCoordY = 0;
    attributes.iSizeW = 0;
    attributes.iSizeH = 0;
    attributes.iAlignmentFlags = ESDCImageAlignNone;

    if( iData->IsScalable() ) {
        attributes.iAttributeFlags |= ESDCImageAttributeNBC;
        }

    int maskIndex = -1;

    while( gInput.NextToken() )
        {
        if( sd_wcsncasecmp( gInput.iToken, L"MASK=", 5 ) == 0 )
            {
            if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
            if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                {
                printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                }
            else
                {
                const wchar_t* maskFile = gInput.iToken+5;
                mbmEntry->iMaskColorDepth = ESDCColorDepth1;
                wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                }
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcsncasecmp( gInput.iToken, L"SOFTMASK=", 9 ) == 0 )
            {
            if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
            if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                {
                printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                }
            else
                {
                const wchar_t* maskFile = gInput.iToken+9;
                mbmEntry->iMaskColorDepth = ESDCColorDepth8;
                wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                }
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"MASK" ) == 0 )
            {
            if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
            mbmEntry->iMaskColorDepth = ESDCColorDepth1;
            maskIndex = mbmIndex+1;
            }
        else if( sd_wcscasecmp( gInput.iToken, L"SOFTMASK" ) == 0 )
            {
            if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
            mbmEntry->iMaskColorDepth = ESDCColorDepth8;
            maskIndex = mbmIndex+1;
            }
        else
            {
            ProcessAttribute( attributes, gInput.iToken );
            }
        }

    if( (maskIndex!=-1) && (attributes.iAttributeFlags&ESDCImageAttributeTile) )
        {
        printf( "WARNING: TILE attribute used with masked bitmap (line %i)\n", gInput.iLineNumber );
        }

    iData->CreateBitmapDef( realIid, mbmIndex, maskIndex, attributes, aAppIcon, restriction );
    return realIid;
    }

void CSDCReader::ProcessAttribute( TSDCImageAttributes& aAttributes, wchar_t* aSource )
    {
    wchar_t buf[512];
    wcscpy( buf, aSource );

    if( sd_wcsncasecmp( buf, L"COORDS=", 7 ) == 0 )
        {
        wchar_t* p = wcsstr( buf, L"," );
        if( !p ) throw CSDCException( ESDCParseError, "No comma in image attribute COORDS" );
        *p = 0;
        aAttributes.iAttributeFlags |= ESDCImageAttributeCoords;
        aAttributes.iCoordX = gInput.ConvertToNumber( buf+7 );
        aAttributes.iCoordY = gInput.ConvertToNumber( p+1 );
        }
    else if( sd_wcsncasecmp( buf, L"SIZE=", 5 ) == 0 )
        {
        wchar_t* p = wcsstr( buf, L"," );
        if( !p ) throw CSDCException( ESDCParseError, "No comma in image attribute SIZE" );
        *p = 0;
        aAttributes.iAttributeFlags |= ESDCImageAttributeSize;
        aAttributes.iSizeW = gInput.ConvertToNumber( buf+5 );
        aAttributes.iSizeH  = gInput.ConvertToNumber( p+1 );
        }
    else if( sd_wcscasecmp( buf, L"STRETCH" ) == 0 )
        {
        aAttributes.iAttributeFlags |= ESDCImageAttributeStretch;
        }
    else if( sd_wcscasecmp( buf, L"TILE" ) == 0 )
        {
        aAttributes.iAttributeFlags |= ESDCImageAttributeTile;
        }
    else if( sd_wcscasecmp( buf, L"TILEX" ) == 0 )
        {
        aAttributes.iAttributeFlags |= ESDCImageAttributeTileX;
        }
    else if( sd_wcscasecmp( buf, L"TILEY" ) == 0 )
        {
        aAttributes.iAttributeFlags |= ESDCImageAttributeTileY;
        }
    else if( sd_wcsncasecmp( buf, L"ALIGN=", 6 ) == 0 )
        {
        if( sd_wcscasecmp( buf+6, L"TL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTL;
        else if( sd_wcscasecmp( buf+6, L"TC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTC;
        else if( sd_wcscasecmp( buf+6, L"TR" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTR;
        else if( sd_wcscasecmp( buf+6, L"CL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCL;
        else if( sd_wcscasecmp( buf+6, L"CC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCC;
        else if( sd_wcscasecmp( buf+6, L"CR" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCR;
        else if( sd_wcscasecmp( buf+6, L"BL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignBL;
        else if( sd_wcscasecmp( buf+6, L"BC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignBC;
        else if( sd_wcscasecmp( buf+6, L"BR" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignBR;
        else throw CSDCException( ESDCParseError, "Unknown parameter for image attribute ALIGN" );

        aAttributes.iAttributeFlags |= ESDCImageAttributeAlign;
        }
    else throw CSDCException( ESDCParseError, "Unknown attribute parameter" );
    }

void CSDCReader::ParseEffectCommand( TSDCEffectCommand& aCommand )
    {
    gInput.NextToken();
    if( sd_wcsncasecmp( gInput.iToken, L"UID=", 4 ) == 0 )
        {
        aCommand.iUid = CSDCInput::ConvertToNumber( gInput.iToken+4 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "UID parameter expected for EFFECT" );
        }

    gInput.NextToken();
    if( sd_wcsncasecmp( gInput.iToken, L"INPUTA=", 7 ) == 0 )
        {
        aCommand.iInputA = CSDCInput::ConvertToLayer( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "INPUTA parameter expected for EFFECT" );
        }

    gInput.NextToken();
    if( sd_wcsncasecmp( gInput.iToken, L"INPUTB=", 7 ) == 0 )
        {
        aCommand.iInputB = CSDCInput::ConvertToLayer( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "INPUTB parameter expected for EFFECT" );
        }

    gInput.NextToken();
    if( sd_wcsncasecmp( gInput.iToken, L"OUTPUT=", 7 ) == 0 )
        {
        aCommand.iOutput = CSDCInput::ConvertToLayer( gInput.iToken+7 );
        }
    else
        {
        throw CSDCException( ESDCParseError, "OUTPUT parameter expected for EFFECT" );
        }

    while( gInput.NextTokenAllowLF() )
        {
        if( sd_wcscasecmp( gInput.iToken, L"END" ) == 0)
            {
            // Just return, everything should be in the struct by now
            return;
            }
        else
            {
            TSDCEffectParameter parameter;
            ParseEffectParameter( parameter );
            aCommand.iParameters.push_back( parameter );
            }
        }

    throw CSDCException( ESDCParseError, "No END token for EFFECT" );
    }

void CSDCReader::ParseEffectParameter( TSDCEffectParameter& aParameter )
    {
    if( sd_wcscasecmp( gInput.iToken, L"INT" ) == 0)
        {
        aParameter.iType = 0;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"STR" ) == 0)
        {
        aParameter.iType = 1;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"BMP" ) == 0)
        {
        aParameter.iType = 2;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"NAMEDREF" ) == 0)
        {
        aParameter.iType = 3;
        }
    else if( sd_wcscasecmp( gInput.iToken, L"RAW" ) == 0)
        {
        aParameter.iType = 4;
        }
    else
        {
        UnexpectedTokenError( gInput.iToken, L"STR, INT, BMP, RAW or NAMEDREF");
        }

    gInput.NextToken();
    wcscpy( aParameter.iName, gInput.iToken );

    if( aParameter.iType == 0 )
        {
        gInput.NextToken();
        aParameter.iNumber =  CSDCInput::ConvertToNumber( gInput.iToken );
        }
    else if( aParameter.iType == 1 )
        {
        gInput.NextStringToken();
        wcscpy( aParameter.iString, gInput.iToken );
        }
    else if( aParameter.iType == 2 )
        {
        // Color depth
        gInput.NextToken();
        aParameter.iColorDepth = CSDCInput::ConvertToColorDepth( gInput.iToken );

        // Filename
        gInput.NextToken();
        bool svg = CSDCInput::IsSvgFile( gInput.iToken );
        TSDCMBMEntry* mbmEntry = iData->AppendMbmEntry( svg, aParameter.iColorDepth, gInput.iToken );
        aParameter.iBmpIndex = mbmEntry->iIndex;

        // Mask
        aParameter.iMaskIndex = -1;
        if( gInput.NextToken() )
            {
            if( sd_wcsncasecmp( gInput.iToken, L"MASK=", 5 ) == 0 )
                {
                if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
                if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                    {
                    printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                    }
                else
                    {
                    const wchar_t* maskFile = gInput.iToken+5;
                    mbmEntry->iMaskColorDepth = ESDCColorDepth1;
                    wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                    }
                aParameter.iMaskIndex = aParameter.iBmpIndex+1;
                }
            else if( sd_wcsncasecmp( gInput.iToken, L"SOFTMASK=", 9 ) == 0 )
                {
                if( svg ) throw CSDCException( ESDCParseError, "SVG icon can not have a named mask" );
                if( mbmEntry->iMaskColorDepth!=ESDCColorDepthNone )
                    {
                    printf("NOTE: Mask already defined, second definition ignored (line %i)\n", gInput.iLineNumber );
                    }
                else
                    {
                    const wchar_t* maskFile = gInput.iToken+9;
                    mbmEntry->iMaskColorDepth = ESDCColorDepth8;
                    wcscpy( mbmEntry->iMaskSourceFilename, maskFile );
                    }
                aParameter.iMaskIndex = aParameter.iBmpIndex+1;
                }
            else if( sd_wcscasecmp( gInput.iToken, L"MASK" ) == 0 )
                {
                if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
                mbmEntry->iMaskColorDepth = ESDCColorDepth1;
                aParameter.iMaskIndex = aParameter.iBmpIndex+1;
                }
            else if( sd_wcscasecmp( gInput.iToken, L"SOFTMASK" ) == 0 )
                {
                if( !svg ) throw CSDCException( ESDCParseError, "Non-SVG icon can not have an unnamed mask" );
                mbmEntry->iMaskColorDepth = ESDCColorDepth8;
                aParameter.iMaskIndex = aParameter.iBmpIndex+1;
                }
            else
                {
                UnexpectedTokenError( gInput.iToken, L"MASK or SOFTMASK" );
                }
            }
        }
    else if( aParameter.iType == 3 )
        {
        gInput.NextToken();
        if( sd_wcsncasecmp( gInput.iToken, L"VALUEID=", 8 ) == 0 )
            {
            aParameter.iNumber = CSDCInput::ConvertToNumber( gInput.iToken+8 );
            }
        else
            {
            throw CSDCException( ESDCParseError, "VALUEID parameter expected for NAMEDREF" );
            }
        }
    else if( aParameter.iType == 4 )
        {
        int tokens = gInput.NextRawToken();
        aParameter.iRawDataCount = tokens;
        memcpy( aParameter.iString, gInput.iToken, tokens * sizeof(wchar_t) );
        }
    }

void CSDCReader::UnexpectedTokenError( wchar_t* aWrongToken, wchar_t* aRightToken )
    {
    char wrongBuf[512];
    gInput.ConvertToAscii( wrongBuf, aWrongToken );
    char rightBuf[512];
    gInput.ConvertToAscii( rightBuf, aRightToken );

    char buf[512];
    strcpy( buf, "Expected \"" );
    strcat( buf, rightBuf );
    strcat( buf, "\", but \"" );
    strcat( buf, wrongBuf );
    strcat( buf, "\" found instead" );
    throw CSDCException( ESDCParseError, buf );
    }

void CSDCReader::UnknownIIDError( wchar_t* aWrongIID )
    {
    char wrongBuf[512];
    gInput.ConvertToAscii( wrongBuf, aWrongIID );

    char buf[512];
    strcpy( buf, "No match found for item ID \"" );
    strcat( buf, wrongBuf );
    strcat( buf, "\"" );
    throw CSDCException( ESDCParseError, buf );
    }

void CSDCReader::StripIIDPrefix( wchar_t* aIID, int& aRestriction )
    {
    wchar_t* delimiter = wcschr( aIID, '%' );
    if( !delimiter )
        {
        return;
        }

    if( iData->iCurrentRestriction )
        throw CSDCException( ESDCParseError, "No item-based restriction can be used where RESTRICTION element already applies" );

    wchar_t buf[512];
    wcscpy( buf, delimiter+1 );

    *delimiter = 0;
    if( sd_wcscasecmp( aIID, L"S60_2_6" ) == 0 )
        {
        aRestriction = 0x0206;
        }
    else if( sd_wcscasecmp( aIID, L"S60_2_7" ) == 0 )
        {
        aRestriction = 0x0207;
        }
    else if( sd_wcscasecmp( aIID, L"S60_2_8" ) == 0 )
        {
        aRestriction = 0x0208;
        }
    else if( sd_wcscasecmp( aIID, L"S60_3_0" ) == 0 )
        {
        aRestriction = 0x0300;
        }
    else if( sd_wcscasecmp( aIID, L"S60_3_1" ) == 0 )
        {
        aRestriction = 0x0301;
        }
    else if( sd_wcscasecmp( aIID, L"S60_3_2" ) == 0 )
        {
        aRestriction = 0x0302;
        }
    else if( sd_wcscasecmp( aIID, L"S60_5_0" ) == 0 )
        {
        aRestriction = 0x0500;
        }
    else if( sd_wcscasecmp( aIID, L"LAY_W" ) == 0 )
        {
        aRestriction = 0x00010000;
        }
    else if( sd_wcscasecmp( aIID, L"LAY_AH" ) == 0 )
        {
        aRestriction = 0x00020000;
        }
    else
        {
        throw CSDCException( ESDCParseError, "Unknown release restriction prefix in IID" );
        }

    wcscpy( aIID, buf );
    }

// End of file
