skins/AknSkins/sdcsrc/SDCReader.cpp
changeset 0 05e9090e2422
child 32 d9c996538b26
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/skins/AknSkins/sdcsrc/SDCReader.cpp	Thu Dec 17 09:14:12 2009 +0200
@@ -0,0 +1,1542 @@
+/*
+* 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"
+
+// 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( _wcsicmp( gInput.iToken, L"SKINTYPE" ) != 0 ) UnexpectedTokenError( gInput.iToken, L"SKINTYPE" );
+    ParseSkintype();
+
+    gInput.NextTokenAllowLF();
+    if( _wcsicmp( gInput.iToken, L"UID" ) == 0 )
+        {
+        ParseUID();
+        gInput.NextTokenAllowLF();
+        }
+    else if( _wcsicmp( gInput.iToken, L"PID" ) == 0 )
+        {
+        ParsePID();
+        gInput.NextTokenAllowLF();
+        }
+    else
+        {
+        GeneratePID();
+        }
+
+    if( _wcsicmp( gInput.iToken, L"NAME" ) != 0 ) UnexpectedTokenError( gInput.iToken, L"NAME" );
+    ParseName();
+
+    while( gInput.NextTokenAllowLF() )
+        {
+        if( _wcsicmp( gInput.iToken, L"NAME" ) == 0 )
+            {
+            ParseName();
+            }
+        else if( _wcsicmp( gInput.iToken, L"LANGUAGEOVERRIDE" ) == 0 )
+            {
+            ParseLanguageOverride();
+            }
+        else if( _wcsicmp( gInput.iToken, L"LANGUAGEPARENT" ) == 0 )
+            {
+            ParseParentUID();
+            }
+        else if( _wcsicmp( gInput.iToken, L"PROTECT" ) == 0 )
+            {
+            ParseProtect();
+            }
+        else if( _wcsicmp( gInput.iToken, L"AUTHOR" ) == 0 )
+            {
+            ParseAuthor();
+            }
+        else if( _wcsicmp( gInput.iToken, L"COPYRIGHT" ) == 0 )
+            {
+            ParseCopyright();
+            }
+        else if( _wcsicmp( gInput.iToken, L"TARGETDEVICE" ) == 0 )
+            {
+            ParseTargetDevice();
+            }
+        else if( _wcsicmp( gInput.iToken, L"TOOL" ) == 0 )
+            {
+            ParseTool();
+            }
+        else if( _wcsicmp( gInput.iToken, L"BITMAP" ) == 0 )
+            {
+            ParseBitmap();
+            }
+        else if( _wcsicmp( gInput.iToken, L"COLORTABLE" ) == 0 )
+            {
+            ParseColorTable();
+            }
+        else if( _wcsicmp( gInput.iToken, L"FRAME" ) == 0 )
+            {
+            ParseFrame();
+            }
+        else if( _wcsicmp( gInput.iToken, L"BMPANIM" ) == 0 )
+            {
+            ParseBmpAnim();
+            }
+        else if( _wcsicmp( gInput.iToken, L"APPICON" ) == 0 )
+            {
+            ParseAppIcon();
+            }
+        else if( _wcsicmp( gInput.iToken, L"PALETTE" ) == 0 )
+            {
+            ParsePalette();
+            }
+        else if( (_wcsicmp( gInput.iToken, L"SOUND" ) == 0)
+            || (_wcsicmp( gInput.iToken, L"STRING" ) == 0) )
+            {
+            ParseString();
+            }
+        else if( _wcsicmp( gInput.iToken, L"SCALABLEITEM" ) == 0 )
+            {
+            if( !iData->IsScalable() ) throw CSDCException( ESDCContentError, "SCALABLEITEM can only be included in scalable skins" );
+            ParseScalableItem();
+            }
+        else if( _wcsicmp( gInput.iToken, L"ANIMATION" ) == 0 )
+            {
+            if( !iData->IsScalable() ) throw CSDCException( ESDCContentError, "ANIMATION can only be included in scalable skins" );
+            ParseAnimation();
+            }
+        else if( _wcsicmp( gInput.iToken, L"BITMAPPATH" ) == 0 )
+            {
+            ParseBitmapPath();
+            }
+        else if( _wcsicmp( 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( _wcsicmp( gInput.iToken, L"normal" ) == 0 )
+        {
+        iData->iSkinType = 0x0;
+        }
+    else if( _wcsicmp( gInput.iToken, L"system" ) == 0 )
+        {
+        iData->iSkinType = 0x1;
+        }
+    else if( _wcsicmp( 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( _wcsicmp( 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( _wcsicmp( 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( _wcsicmp( 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( _wcsicmp( gInput.iToken, L"CLASS=appicon" ) == 0 )
+        {
+        appIcon = true;
+        gInput.NextToken();
+        }
+
+    int restriction( iData->iCurrentRestriction );
+    const TSDCIIDEntry* iid = NULL;
+    if( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsicmp( gInput.iToken, L"END" ) == 0 )
+            {
+            iData->CreateColorTableDef( realIid, colors, restriction );
+            return;
+            }
+        else if( _wcsnicmp( gInput.iToken, L"IDX=", 4 ) == 0 )
+            {
+            TSDCColorTableEntry entry;
+            entry.iIndex = gInput.ConvertToNumber( gInput.iToken+4 );
+            entry.iRgb = 0;
+            colors.push_back( entry );
+            }
+        else if( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( gInput.iToken, L"INTERVAL=", 9 ) == 0 )
+            {
+            interval = gInput.ConvertToNumber( gInput.iToken+9 );
+            }
+        else if( _wcsicmp( gInput.iToken, L"MODE=play" ) == 0 )
+            {
+            playMode = 0;
+            }
+        else if( _wcsicmp( gInput.iToken, L"MODE=cycle" ) == 0 )
+            {
+            playMode = 1;
+            }
+        else if( _wcsicmp( gInput.iToken, L"MODE=bounce" ) == 0 )
+            {
+            playMode = 2;
+            }
+        else if( _wcsicmp( gInput.iToken, L"MODE=svganim" ) == 0 )
+            {
+            playMode = 255;
+            svganim = true;
+            }
+        else if( _wcsicmp( 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( _wcsnicmp( gInput.iToken, L"TIME=", 5 ) == 0 )
+            {
+            time = gInput.ConvertToNumber( gInput.iToken+5 );
+            }
+        else if( _wcsnicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( gInput.iToken, L"INPUT=", 6 ) == 0 )
+        {
+        input = CSDCInput::ConvertToLayer( gInput.iToken+6 );
+        }
+    else if( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( gInput.iToken, L"END" ) == 0)
+            {
+            iData->CreateScalableItemDef( realIid, input, output, commands, restriction );
+            return;
+            }
+        else if( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( gInput.iToken, L"MORPHING" ) == 0 )
+        {
+        morphing = true;
+
+        gInput.NextTokenAllowLF();
+        }
+
+    vector<TSDCEffectCommand> preprocessCommands;
+    if( _wcsicmp( gInput.iToken, L"PREPROCESS" ) == 0 )
+        {
+        bool terminated( false );
+        while( gInput.NextTokenAllowLF() )
+            {
+            if( _wcsicmp( gInput.iToken, L"END" ) == 0)
+                {
+                terminated = true;
+                break;
+                }
+            else if( _wcsicmp( 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( _wcsicmp( gInput.iToken, L"END" ) == 0)
+            {
+            terminated = true;
+            break;
+            }
+        else if( _wcsicmp( gInput.iToken, L"COMMAND" ) == 0)
+            {
+            TSDCEffectCommand command;
+            ParseEffectCommand( command );
+            animCommands.push_back( command );
+            }
+        else if( _wcsicmp( gInput.iToken, L"VALUE" ) == 0)
+            {
+            gInput.NextToken();
+            if( _wcsnicmp( gInput.iToken, L"UID=", 4 ) )
+                throw new CSDCException( ESDCParseError, "UID parameter expected for VALUE" );
+            int uid = CSDCInput::ConvertToNumber( gInput.iToken+4 );
+
+            gInput.NextToken();
+            if( _wcsnicmp( 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() && _wcsicmp( 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( _wcsicmp( gInput.iToken, L"TIMINGMODEL" ) == 0)
+            {
+            gInput.NextToken();
+            if( _wcsnicmp( 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() && _wcsicmp( 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( _wcsicmp( gInput.iToken, L"SIZEBOUNDPARAM" ) == 0)
+            {
+            gInput.NextToken();
+            TSDCEffectParameter parameter;
+            parameter.iType = 0;
+            wcscpy( parameter.iName, gInput.iToken );
+            parameter.iNumber = 0;
+
+            gInput.NextToken();
+            if( _wcsnicmp( 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( _wcsicmp( gInput.iToken, L"FLAGS=W" ) == 0 )
+                {
+                flags = 1;
+                }
+            else if( _wcsicmp( gInput.iToken, L"FLAGS=H" ) == 0 )
+                {
+                flags = 2;
+                }
+            else if( _wcsicmp( 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( _wcsicmp( 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( _wcsicmp( gInput.iToken, L"S60_2_6" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0206;
+        printf("NOTE: Current restriction set to 2.6\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_2_7" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0207;
+        printf("NOTE: Current restriction set to 2.7\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_2_8" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0208;
+        printf("NOTE: Current restriction set to 2.8\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_3_0" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0300;
+        printf("NOTE: Current restriction set to 3.0\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_3_1" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0301;
+        printf("NOTE: Current restriction set to 3.1\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_3_2" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0302;
+        printf("NOTE: Current restriction set to 3.2\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"S60_5_0" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x0500;
+        printf("NOTE: Current restriction set to 5.0\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"LAY_W" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x00010000;
+        printf("NOTE: Current restriction set to Layout/Non-Mirrored\n");
+        }
+    else if( _wcsicmp( gInput.iToken, L"LAY_AH" ) == 0 )
+        {
+        iData->iCurrentRestriction = 0x00020000;
+        printf("NOTE: Current restriction set to Layout/Mirrored\n");
+        }
+    else if( _wcsnicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( buf, L"STRETCH" ) == 0 )
+        {
+        aAttributes.iAttributeFlags |= ESDCImageAttributeStretch;
+        }
+    else if( _wcsicmp( buf, L"TILE" ) == 0 )
+        {
+        aAttributes.iAttributeFlags |= ESDCImageAttributeTile;
+        }
+    else if( _wcsicmp( buf, L"TILEX" ) == 0 )
+        {
+        aAttributes.iAttributeFlags |= ESDCImageAttributeTileX;
+        }
+    else if( _wcsicmp( buf, L"TILEY" ) == 0 )
+        {
+        aAttributes.iAttributeFlags |= ESDCImageAttributeTileY;
+        }
+    else if( _wcsnicmp( buf, L"ALIGN=", 6 ) == 0 )
+        {
+        if( _wcsicmp( buf+6, L"TL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTL;
+        else if( _wcsicmp( buf+6, L"TC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTC;
+        else if( _wcsicmp( buf+6, L"TR" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignTR;
+        else if( _wcsicmp( buf+6, L"CL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCL;
+        else if( _wcsicmp( buf+6, L"CC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCC;
+        else if( _wcsicmp( buf+6, L"CR" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignCR;
+        else if( _wcsicmp( buf+6, L"BL" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignBL;
+        else if( _wcsicmp( buf+6, L"BC" ) == 0 ) aAttributes.iAlignmentFlags = ESDCImageAlignBC;
+        else if( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsicmp( gInput.iToken, L"INT" ) == 0)
+        {
+        aParameter.iType = 0;
+        }
+    else if( _wcsicmp( gInput.iToken, L"STR" ) == 0)
+        {
+        aParameter.iType = 1;
+        }
+    else if( _wcsicmp( gInput.iToken, L"BMP" ) == 0)
+        {
+        aParameter.iType = 2;
+        }
+    else if( _wcsicmp( gInput.iToken, L"NAMEDREF" ) == 0)
+        {
+        aParameter.iType = 3;
+        }
+    else if( _wcsicmp( 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( _wcsnicmp( 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( _wcsnicmp( 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( _wcsicmp( 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( _wcsicmp( 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( _wcsnicmp( 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( _wcsicmp( aIID, L"S60_2_6" ) == 0 )
+        {
+        aRestriction = 0x0206;
+        }
+    else if( _wcsicmp( aIID, L"S60_2_7" ) == 0 )
+        {
+        aRestriction = 0x0207;
+        }
+    else if( _wcsicmp( aIID, L"S60_2_8" ) == 0 )
+        {
+        aRestriction = 0x0208;
+        }
+    else if( _wcsicmp( aIID, L"S60_3_0" ) == 0 )
+        {
+        aRestriction = 0x0300;
+        }
+    else if( _wcsicmp( aIID, L"S60_3_1" ) == 0 )
+        {
+        aRestriction = 0x0301;
+        }
+    else if( _wcsicmp( aIID, L"S60_3_2" ) == 0 )
+        {
+        aRestriction = 0x0302;
+        }
+    else if( _wcsicmp( aIID, L"S60_5_0" ) == 0 )
+        {
+        aRestriction = 0x0500;
+        }
+    else if( _wcsicmp( aIID, L"LAY_W" ) == 0 )
+        {
+        aRestriction = 0x00010000;
+        }
+    else if( _wcsicmp( aIID, L"LAY_AH" ) == 0 )
+        {
+        aRestriction = 0x00020000;
+        }
+    else
+        {
+        throw CSDCException( ESDCParseError, "Unknown release restriction prefix in IID" );
+        }
+
+    wcscpy( aIID, buf );
+    }
+
+// End of file