upnpavcontroller/upnpavcontrollerserver/src/upnpavdeviceextended.cpp
branchnew development branch with rendering state machine and other goodies
changeset 38 5360b7ddc251
parent 32 3785f754ee62
equal deleted inserted replaced
32:3785f754ee62 38:5360b7ddc251
    19 
    19 
    20 
    20 
    21 
    21 
    22 
    22 
    23 // INCLUDES
    23 // INCLUDES
    24 // upnp stack api
    24 #include <in_sock.h>
       
    25 
       
    26 // dlnasrv / mediaserver api
    25 #include <upnpdlnaprotocolinfo.h>
    27 #include <upnpdlnaprotocolinfo.h>
    26 #include <upnpitem.h>
    28 #include <upnpitem.h>
    27 
    29 
    28 // upnpframework / avcontroller helper api
    30 // dlnasrv / avcontroller helper api
    29 #include "upnpitemutility.h"
    31 #include "upnpitemutility.h"
    30 #include "upnpconstantdefs.h" // for upnp-specific stuff
    32 #include "upnpconstantdefs.h" // for upnp-specific stuff
    31 
    33 
    32 // upnpframework / internal api's
    34 // dlnasrv / internal api's
    33 #include "upnpcommonutils.h"
    35 #include "upnpcommonutils.h"
    34 
    36 
    35 // avcontroller internal
    37 // dlnasrv / avcontroller internal
    36 #include "upnpavdeviceextended.h"
    38 #include "upnpavdeviceextended.h"
    37 
    39 
    38 
    40 
    39 _LIT8( KAudioSupport,        "audio/" );
    41 _LIT8( KAudioSupport,        "audio/" );
    40 _LIT8( KImageSupport,        "image/" );
    42 _LIT8( KImageSupport,        "image/" );
    49 const TInt KUnicodeC1RangeEnd = 159;// the end character of C1 range
    51 const TInt KUnicodeC1RangeEnd = 159;// the end character of C1 range
    50 const TInt KSlash = 92;
    52 const TInt KSlash = 92;
    51 _LIT8( KProtocolInfo,       "protocolInfo" );
    53 _LIT8( KProtocolInfo,       "protocolInfo" );
    52 _LIT8( KAsterisk,           "*" );
    54 _LIT8( KAsterisk,           "*" );
    53 
    55 
       
    56 const TInt KUrlIpBufLen = 16; // For IP address e.g. xxx.xxx.xxx.xxx
       
    57 const TInt KUrlPortLen = 11; // For HTTP port e.g. yyyyy
       
    58 _LIT8( KUrlColon, ":" );
       
    59 _LIT8( KUrlSlash, "/" );
       
    60 _LIT8( KUrlHttp, "http://" );
       
    61 
    54 _LIT( KComponentLogfile, "upnpavcontrollerserver.txt");
    62 _LIT( KComponentLogfile, "upnpavcontrollerserver.txt");
    55 #include "upnplog.h"
    63 #include "upnplog.h"
       
    64 
       
    65 // --------------------------------------------------------------------------
       
    66 // EnsureFinalSlash
       
    67 // --------------------------------------------------------------------------
       
    68 static void EnsureFinalSlash( TDes8& aUrl )
       
    69     {
       
    70     TInt len( aUrl.Length() );
       
    71     if ( len > 0 && aUrl[ len - 1 ] != KUrlSlash()[ 0 ] )
       
    72         {
       
    73         aUrl.Append( KUrlSlash );
       
    74         }
       
    75     }
       
    76 
       
    77 // --------------------------------------------------------------------------
       
    78 // RemoveInitial
       
    79 // --------------------------------------------------------------------------
       
    80 static TPtrC8 RemoveInitial( const TDesC8& aUrl, const TDesC8& aInitial )
       
    81     {
       
    82     return aUrl.Find( aInitial ) == 0 ? aUrl.Mid( aInitial.Length() ) : aUrl;
       
    83     }
    56 
    84 
    57 // ============================ MEMBER FUNCTIONS ============================
    85 // ============================ MEMBER FUNCTIONS ============================
    58 
    86 
    59 // --------------------------------------------------------------------------
    87 // --------------------------------------------------------------------------
    60 // CUpnpAVDeviceExtended::NewL
    88 // CUpnpAVDeviceExtended::NewL
    78     dev->iImageMediaCapability = aDevice.ImageCapability();
   106     dev->iImageMediaCapability = aDevice.ImageCapability();
    79     dev->iVideoMediaCapability = aDevice.VideoCapability();
   107     dev->iVideoMediaCapability = aDevice.VideoCapability();
    80     dev->iNextAVTransportUri = aDevice.NextAVTransportUri();
   108     dev->iNextAVTransportUri = aDevice.NextAVTransportUri();
    81     dev->iMaxVolume = aDevice.MaxVolume();
   109     dev->iMaxVolume = aDevice.MaxVolume();
    82     dev->iDlnaCompatible = aDevice.DlnaCompatible();
   110     dev->iDlnaCompatible = aDevice.DlnaCompatible();
       
   111     dev->iSeekCapability = aDevice.SeekCapability();
    83            
   112            
    84     dev->ConstructL();
   113     dev->ConstructL();
    85     CleanupStack::Pop();
   114     CleanupStack::Pop();
    86     return dev;
   115     return dev;
    87     }
   116     }
   108     dev->iImageMediaCapability = aDevice.ImageCapability();
   137     dev->iImageMediaCapability = aDevice.ImageCapability();
   109     dev->iVideoMediaCapability = aDevice.VideoCapability();
   138     dev->iVideoMediaCapability = aDevice.VideoCapability();
   110     dev->iNextAVTransportUri = aDevice.NextAVTransportUri();
   139     dev->iNextAVTransportUri = aDevice.NextAVTransportUri();
   111     dev->iMaxVolume = aDevice.MaxVolume();
   140     dev->iMaxVolume = aDevice.MaxVolume();
   112     dev->iDlnaCompatible = aDevice.DlnaCompatible();
   141     dev->iDlnaCompatible = aDevice.DlnaCompatible();
       
   142     dev->iSeekCapability = aDevice.SeekCapability();
   113         
   143         
   114     dev->SetSinkProtocolInfoL( aDevice.SinkProtocolInfo() );
   144     dev->SetSinkProtocolInfoL( aDevice.SinkProtocolInfo() );
   115     dev->SetSourceProtocolInfoL( aDevice.SourceProtocolInfo() );
   145     dev->SetSourceProtocolInfoL( aDevice.SourceProtocolInfo() );
   116     dev->iSubscriptionCount = aDevice.SubscriptionCount();   
   146     dev->iSubscriptionCount = aDevice.SubscriptionCount();   
   117     dev->iLocal = aDevice.Local();
   147     dev->iLocal = aDevice.Local();
   120     dev->iVideoUpload = aDevice.VideoUpload(); 
   150     dev->iVideoUpload = aDevice.VideoUpload(); 
   121     dev->iCreateChildContainer = aDevice.CreateChildContainer();
   151     dev->iCreateChildContainer = aDevice.CreateChildContainer();
   122     dev->iDestroyObject = aDevice.DestroyObject();
   152     dev->iDestroyObject = aDevice.DestroyObject();
   123     dev->iPInfoReceived = aDevice.PInfoReceived();
   153     dev->iPInfoReceived = aDevice.PInfoReceived();
   124     dev->iDLNADeviceType = aDevice.DLNADeviceType();
   154     dev->iDLNADeviceType = aDevice.DLNADeviceType();
       
   155     dev->iPrepareForConnection = aDevice.PrepareForConnection();
   125     
   156     
   126     dev->ConstructL();
   157     dev->ConstructL();
   127     CleanupStack::Pop();
   158     CleanupStack::Pop();
   128     return dev;
   159     return dev;
   129     }
   160     }
   144 // --------------------------------------------------------------------------
   175 // --------------------------------------------------------------------------
   145 // CUpnpAVDeviceExtended::~CUpnpAVDeviceExtended
   176 // CUpnpAVDeviceExtended::~CUpnpAVDeviceExtended
   146 // See upnpavdeviceextended.h
   177 // See upnpavdeviceextended.h
   147 // --------------------------------------------------------------------------
   178 // --------------------------------------------------------------------------
   148 CUpnpAVDeviceExtended::~CUpnpAVDeviceExtended()
   179 CUpnpAVDeviceExtended::~CUpnpAVDeviceExtended()
   149     {     
   180     {
       
   181     delete iIconUrl;
   150     iSinkProtocolInfo.ResetAndDestroy();
   182     iSinkProtocolInfo.ResetAndDestroy();
   151     iSourceProtocolInfo.ResetAndDestroy();
   183     iSourceProtocolInfo.ResetAndDestroy();
   152     }
   184     }
   153     
   185     
   154 
   186 
   223                 input.MarkedToken() ) );
   255                 input.MarkedToken() ) );
   224              
   256              
   225             if( err == KErrNone && tmpInfo )
   257             if( err == KErrNone && tmpInfo )
   226                 {
   258                 {
   227                 // Transfer ownership of tmpInfo
   259                 // Transfer ownership of tmpInfo
   228                 iSinkProtocolInfo.AppendL( tmpInfo );
   260                 iSinkProtocolInfo.Append( tmpInfo );
   229                 }
   261                 }
   230             else
   262             else
   231                 {
   263                 {
   232                 __LOG1( "CUpnpDlnaProtocolInfo::NewL failed: %d", err );
   264                 __LOG1( "CUpnpDlnaProtocolInfo::NewL failed: %d", err );
   233                 }
   265                 }
   274                 input.MarkedToken() ) );
   306                 input.MarkedToken() ) );
   275                
   307                
   276             if( err == KErrNone && tmpInfo )
   308             if( err == KErrNone && tmpInfo )
   277                 {
   309                 {
   278                 // Transfer ownership of tmpInfo
   310                 // Transfer ownership of tmpInfo
   279                 iSourceProtocolInfo.AppendL( tmpInfo );
   311                 iSourceProtocolInfo.Append( tmpInfo );
   280                 }
   312                 }
   281             else
   313             else
   282                 {
   314                 {
   283                 __LOG1( "CUpnpDlnaProtocolInfo::NewL failed: %d", err );
   315                 __LOG1( "CUpnpDlnaProtocolInfo::NewL failed: %d", err );
   284                 }
   316                 }
   329     {
   361     {
   330     __LOG( "CUpnpAVDeviceExtended::MatchSinkProtocolInfo" );
   362     __LOG( "CUpnpAVDeviceExtended::MatchSinkProtocolInfo" );
   331 
   363 
   332     TBool match = EFalse;
   364     TBool match = EFalse;
   333 
   365 
   334     if( DlnaCompatible() )
   366     if( DlnaCompatible() && DLNADeviceType() == CUpnpAVDeviceExtended::EDMR )
   335         {
   367         {
   336         // The device is DLNA compatible
   368         // The device is DLNA compatible
   337         
   369         
   338         // Try try find PN parameter to determine if it's dlna content
   370         // Try try find PN parameter to determine if it's dlna content
   339         if( aInfo.Find( KDlnaPn ) != KErrNotFound )
   371         if( aInfo.Find( KDlnaPn ) != KErrNotFound )
   340             {
   372             {
   341             __LOG( "MatchSinkProtocolInfo - DLNA content and the renderer \
   373             __LOG( "MatchSinkProtocolInfo - DLNA content and the renderer \
   342 is DLNA compatible, start matching..." );
   374 is DLNA compatible, start matching..." );
   343 
   375 
   344             match = MatchSinkProfileId( aInfo );
   376             match = MatchSinkProfileId( aInfo );
       
   377             if( !match )
       
   378                 {
       
   379                 // if profile id was not found try matching mime 
       
   380                 match = MatchSinkMime( aInfo );
       
   381                 }
   345             }
   382             }
   346         else
   383         else
   347             {
   384             {
   348             __LOG( "MatchSinkProtocolInfo - Non DLNA content and the \
   385             __LOG( "MatchSinkProtocolInfo - Non DLNA content and the \
   349 renderer is DLNA compatible, start matching..." );            
   386 renderer is DLNA compatible, start matching..." );            
   396         // Match the first parameter and PN parameter
   433         // Match the first parameter and PN parameter
   397         TInt count = iSinkProtocolInfo.Count();
   434         TInt count = iSinkProtocolInfo.Count();
   398         for( TInt i = 0; i < count; i++ )
   435         for( TInt i = 0; i < count; i++ )
   399             {
   436             {
   400             if( iSinkProtocolInfo[ i ]->PnParameter() ==
   437             if( iSinkProtocolInfo[ i ]->PnParameter() ==
   401                 tmpInfo->PnParameter() ||
   438                 tmpInfo->PnParameter() )
   402                 iSinkProtocolInfo[ i ]->FourthField() == KAsterisk )
       
   403                 {
   439                 {
   404                 // PN parameter matches, try matching the first
   440                 // PN parameter matches, try matching the first
   405                 // parameter
   441                 // parameter
   406                 if( iSinkProtocolInfo[ i ]->FirstField() ==
   442                 if( iSinkProtocolInfo[ i ]->FirstField() ==
   407                     tmpInfo->FirstField() ||
   443                     tmpInfo->FirstField() ||
   608     {
   644     {
   609     __LOG( "CUpnpAVDeviceExtended::SetCapabilitiesBySupportedMimeTypesL" );
   645     __LOG( "CUpnpAVDeviceExtended::SetCapabilitiesBySupportedMimeTypesL" );
   610     
   646     
   611     if( aListOfMimeTypes != KNullDesC8 )
   647     if( aListOfMimeTypes != KNullDesC8 )
   612         {
   648         {
   613  	    // Update the audio media capability
   649         // Update the audio media capability
   614         if( UPnPCommonUtils::IsAudioSupported( aListOfMimeTypes ) )
   650         if( UPnPCommonUtils::IsAudioSupported( aListOfMimeTypes ) )
   615             {
   651             {
   616             iAudioMediaCapability = ETrue;
   652             iAudioMediaCapability = ETrue;
   617             }
   653             }
   618         else
   654         else
   619             {	
   655             {
   620             iAudioMediaCapability = EFalse;
   656             iAudioMediaCapability = EFalse;
   621             }
   657             }
   622 
   658 
   623 	    // Update the audio media capability
   659         // Update the audio media capability
   624         if( UPnPCommonUtils::IsImageSupported( aListOfMimeTypes ) )
   660         if( UPnPCommonUtils::IsImageSupported( aListOfMimeTypes ) )
   625             {
   661             {
   626             iImageMediaCapability = ETrue;
   662             iImageMediaCapability = ETrue;
   627             }
   663             }
   628         else
   664         else
   629             {	
   665             {
   630             iImageMediaCapability = EFalse;
   666             iImageMediaCapability = EFalse;
   631             }
   667             }
   632   
   668   
   633         // Update the video media capability
   669         // Update the video media capability
   634         if( UPnPCommonUtils::IsVideoSupported( aListOfMimeTypes ) )
   670         if( UPnPCommonUtils::IsVideoSupported( aListOfMimeTypes ) )
   635             {
   671             {
   636             iVideoMediaCapability = ETrue;
   672             iVideoMediaCapability = ETrue;
   637             }
   673             }
   638         else
   674         else
   639             {	
   675             {
   640             iVideoMediaCapability = EFalse;
   676             iVideoMediaCapability = EFalse;
   641             }
   677             }
   642         }
   678         }
   643     }
   679     }
   644 
   680 
   653     
   689     
   654     for( TInt i = 0; i < aProtocolInfo.Count(); i++ )
   690     for( TInt i = 0; i < aProtocolInfo.Count(); i++ )
   655         {
   691         {
   656         CUpnpDlnaProtocolInfo* tmpInfo = CUpnpDlnaProtocolInfo::NewL(
   692         CUpnpDlnaProtocolInfo* tmpInfo = CUpnpDlnaProtocolInfo::NewL(
   657             aProtocolInfo[ i ]->ProtocolInfoL() );
   693             aProtocolInfo[ i ]->ProtocolInfoL() );
   658         iSourceProtocolInfo.AppendL( tmpInfo ); // Ownership is transferred
   694         iSourceProtocolInfo.Append( tmpInfo ); // Ownership is transferred
   659         }    
   695         }    
   660     }
   696     }
   661     
   697     
   662 // --------------------------------------------------------------------------
   698 // --------------------------------------------------------------------------
   663 // CUpnpAVDeviceExtended::SetSinkProtocolInfoL
   699 // CUpnpAVDeviceExtended::SetSinkProtocolInfoL
   670     
   706     
   671     for( TInt i = 0; i < aProtocolInfo.Count(); i++ )
   707     for( TInt i = 0; i < aProtocolInfo.Count(); i++ )
   672         {
   708         {
   673         CUpnpDlnaProtocolInfo* tmpInfo = CUpnpDlnaProtocolInfo::NewL(
   709         CUpnpDlnaProtocolInfo* tmpInfo = CUpnpDlnaProtocolInfo::NewL(
   674             aProtocolInfo[ i ]->ProtocolInfoL() );
   710             aProtocolInfo[ i ]->ProtocolInfoL() );
   675         iSinkProtocolInfo.AppendL( tmpInfo ); // Ownership is transferred
   711         iSinkProtocolInfo.Append( tmpInfo ); // Ownership is transferred
   676         }
   712         }
   677     }
   713     }
   678     
   714     
   679 // --------------------------------------------------------------------------
   715 // --------------------------------------------------------------------------
   680 // CUpnpAVDeviceExtended::ParseToDelimeter
   716 // CUpnpAVDeviceExtended::ParseToDelimeter
   681 // See upnpavdeviceextended.h
   717 // See upnpavdeviceextended.h
   682 // --------------------------------------------------------------------------
   718 // --------------------------------------------------------------------------
   683 void CUpnpAVDeviceExtended::ParseToDelimeter( TLex8& aLex, TChar aDelimeter )
   719 void CUpnpAVDeviceExtended::ParseToDelimeter( TLex8& aLex, TChar aDelimeter )
   684     {
   720     {
   685     aLex.Mark();
   721     aLex.Mark();
   686         
   722 
   687     TChar chr = 0;
   723     TChar chr = 0;
   688 	TChar edchr = 0;
   724     TChar edchr = 0;
   689 		
   725 
   690 	while( !aLex.Eos() )
   726     while( !aLex.Eos() )
   691 		{
   727         {
   692 		edchr = chr;
   728         edchr = chr;
   693 		
   729         chr = aLex.Peek();
   694 		chr = aLex.Peek();
   730         if( chr == aDelimeter && edchr != TChar( KSlash ) )
   695 		if( chr == aDelimeter && edchr != TChar( KSlash ) )
   731             {
   696 			{
   732             break;
   697 			break;
   733             }
   698 			}
   734         aLex.Inc();
   699 			
   735         }
   700 		aLex.Inc();		
   736     }
   701 		}
   737 
   702     }
       
   703     
       
   704 // --------------------------------------------------------------------------
   738 // --------------------------------------------------------------------------
   705 // CUpnpAVDeviceExtended::RemoveIllegalCharactersL
   739 // CUpnpAVDeviceExtended::RemoveIllegalCharactersL
   706 // See upnpavdeviceextended.h
   740 // See upnpavdeviceextended.h
   707 // --------------------------------------------------------------------------   
   741 // --------------------------------------------------------------------------
   708  HBufC8* CUpnpAVDeviceExtended::RemoveIllegalCharactersL(
   742  HBufC8* CUpnpAVDeviceExtended::RemoveIllegalCharactersL(
   709     const TDesC8& aPtr ) const
   743     const TDesC8& aPtr ) const
   710     {
   744     {
   711     HBufC8* ptrResult = NULL;
   745     HBufC8* ptrResult = NULL;
   712     TInt i = KErrNotFound;
   746     TInt i = KErrNotFound;
   856 TBool CUpnpAVDeviceExtended::PInfoReceived() const
   890 TBool CUpnpAVDeviceExtended::PInfoReceived() const
   857     {
   891     {
   858     return iPInfoReceived;
   892     return iPInfoReceived;
   859     }
   893     }
   860 
   894 
       
   895 // --------------------------------------------------------------------------
       
   896 // CUpnpAVDeviceExtended::PrepareForConnection
       
   897 // See upnpavdeviceextended.h
       
   898 // --------------------------------------------------------------------------
       
   899 TBool CUpnpAVDeviceExtended::PrepareForConnection() const
       
   900     {
       
   901     return iPrepareForConnection;
       
   902     }
       
   903 
       
   904 // --------------------------------------------------------------------------
       
   905 // CUpnpAVDeviceExtended::SetPrepareForConnection
       
   906 // See upnpavdeviceextended.h
       
   907 // --------------------------------------------------------------------------
       
   908 void CUpnpAVDeviceExtended::SetPrepareForConnection(
       
   909     TBool aPrepareForConnection )
       
   910     {
       
   911     iPrepareForConnection = aPrepareForConnection;
       
   912     }
       
   913 
       
   914 // --------------------------------------------------------------------------
       
   915 // CUpnpAVDeviceExtended::SetDLNADeviceType
       
   916 // See upnpavdeviceextended.h
       
   917 // --------------------------------------------------------------------------
   861 void CUpnpAVDeviceExtended::SetDLNADeviceType( TDLNADeviceType aDeviceType )
   918 void CUpnpAVDeviceExtended::SetDLNADeviceType( TDLNADeviceType aDeviceType )
   862     {
   919     {
   863     iDLNADeviceType = aDeviceType;
   920     iDLNADeviceType = aDeviceType;
   864     }
   921     }
   865    
   922    
       
   923 // --------------------------------------------------------------------------
       
   924 // CUpnpAVDeviceExtended::DLNADeviceType
       
   925 // See upnpavdeviceextended.h
       
   926 // --------------------------------------------------------------------------
   866 CUpnpAVDeviceExtended::TDLNADeviceType
   927 CUpnpAVDeviceExtended::TDLNADeviceType
   867     CUpnpAVDeviceExtended::DLNADeviceType() const
   928     CUpnpAVDeviceExtended::DLNADeviceType() const
   868     {
   929     {
   869     return iDLNADeviceType;
   930     return iDLNADeviceType;
   870     }
   931     }
   871 
   932 
   872 
   933 // --------------------------------------------------------------------------
   873     
   934 // CUpnpAVDeviceExtended::SetIconUrlL
       
   935 // See upnpavdeviceextended.h
       
   936 // --------------------------------------------------------------------------
       
   937 void CUpnpAVDeviceExtended::SetIconUrlL( const TInetAddr& aAddress,
       
   938     const TDesC8& aUrlBase, const TDesC8& aIconUrl )
       
   939     {
       
   940     __LOG8_1( "CUpnpAVDeviceExtended::SetIconUrlL - Base: %S", &aUrlBase );
       
   941     __LOG8_1( "CUpnpAVDeviceExtended::SetIconUrlL - Icon: %S", &aIconUrl );
       
   942     TBuf< KUrlIpBufLen > ip;
       
   943     aAddress.Output( ip );
       
   944     HBufC8* iconUrl = HBufC8::NewL( KUrlHttp().Length() + ip.Length() + 1 + // 1 for colon
       
   945                                     KUrlPortLen + 1 + // 1 for slash
       
   946                                     aUrlBase.Length() + 1 + // 1 for slash
       
   947                                     aIconUrl.Length() );
       
   948     TPtr8 ptr( iconUrl->Des() );
       
   949     if ( aUrlBase.Find( KUrlHttp ) == KErrNotFound )
       
   950         {
       
   951         ptr.Copy( KUrlHttp );
       
   952         ptr.Append( ip );
       
   953         ptr.Append( KUrlColon );
       
   954         ptr.AppendNumUC( aAddress.Port() );
       
   955         }
       
   956     EnsureFinalSlash( ptr );
       
   957 
       
   958     TPtrC8 urlBase( RemoveInitial( aUrlBase, KUrlSlash ) ); // Url base without initial slash
       
   959     ptr.Append( urlBase );
       
   960     EnsureFinalSlash( ptr );
       
   961 
       
   962     TPtrC8 icon( RemoveInitial( aIconUrl, KUrlSlash ) ); // Icon url without initial slash
       
   963     ptr.Append( RemoveInitial( icon, urlBase ) ); // Remove base from icon url if it already contains base
       
   964 
       
   965     delete iIconUrl;
       
   966     iIconUrl = iconUrl;
       
   967 
       
   968     __LOG8_1( "CUpnpAVDeviceExtended::SetIconUrlL - Device: %S", &( FriendlyName() ) );
       
   969     __LOG8_1( "CUpnpAVDeviceExtended::SetIconUrlL - Url: %S", iIconUrl );
       
   970     }
       
   971 
       
   972 // --------------------------------------------------------------------------
       
   973 // CUpnpAVDeviceExtended::IconUrl
       
   974 // See upnpavdeviceextended.h
       
   975 // --------------------------------------------------------------------------
       
   976 TPtrC8 CUpnpAVDeviceExtended::IconUrl() const
       
   977     {
       
   978     return ( iIconUrl ? *iIconUrl : KNullDesC8() );
       
   979     }
       
   980 
   874 // end of file
   981 // end of file