From 859537bbf43dfea1d3156baec841930267e3289f Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Wed, 19 Feb 2014 11:27:07 +0000 Subject: [PATCH] TestEGr 1.5b5 : - aggiunto comando Nuovo - aggiunta comando Esegui Tsc --- TestEGr.cpp | 2 + TestEGr.rc | Bin 19646 -> 20120 bytes TestEGr.vcxproj | 3 + TestEGr.vcxproj.filters | 9 +++ TestEGrDlg.cpp | 142 +++++++++++++++++++++++++++++++++++----- TestEGrDlg.h | 11 +++- TestEGrUtils.cpp | 39 +++++++++++ TestEGrUtils.h | 20 ++++++ TestEGrView.h | 9 ++- TestEgrView.cpp | 33 ++++++++-- res/Logo.bmp | Bin 0 -> 6966 bytes resource.h | Bin 3010 -> 3466 bytes 12 files changed, 240 insertions(+), 28 deletions(-) create mode 100644 TestEGrUtils.cpp create mode 100644 TestEGrUtils.h create mode 100644 res/Logo.bmp diff --git a/TestEGr.cpp b/TestEGr.cpp index d9326e4..cc7da86 100644 --- a/TestEGr.cpp +++ b/TestEGr.cpp @@ -92,6 +92,8 @@ CTestEGrApp::InitInstance( void) // inizializzazioni del logger Logger logger( ( nDebug > 0 ? LL_DEBUG : LL_INFO), "TestEGr") ; logger.AddOutputStream( new ofstream( "TestEgr.log"), true) ; + SetEGkLogger( &logger) ; + SetEGrLogger( &logger) ; // inizio programma LOG_DATETIME( &logger, " Start") diff --git a/TestEGr.rc b/TestEGr.rc index 9d628181e064b6339d30d38d7e5620e6f7551776..bd62871f6d4d5e01fdd344cd39f53fdb90160a85 100644 GIT binary patch delta 542 zcmXYu%S#(k6vn?v&`d`2n8ZZ`MW%`%8Z?tJiDPk*#7T_A@zL=?H&#W|fN2PYx^OAo z*Mp!71#x9pEw}4#+EpubTZ+4ybT8g~n1FC2Vnou9E893nUPk60i5e|x|v##MO>l$V` zuChuqYwB zi!HoFAJa8Vq0Ak+oPUL5>;E&2(?#SleNPynoUA3|kb`|DH|d?44436?8#(%uxS+e} z5Zy#n(Th&O3QpQgXjKKdPN(zke<-bZJ|k>0C_^W@Kzcr$PvpyfB=FFovXRZn z;bkVMRA3J}a13?GLj@X8qApKe5g$dxpRi~u(W0=ZtrIgs0DEE#FElrP30Yo;4DN{6 zd!+a7~nZNdSxpg_5Y6G6s?%Z<;XGq*sd39VUsG|;Ioz&@!e zkS5d8)*|s&6pQ;3)JUbtzU|9Ad+<~_9LQCiNg8mYE{BOQL&?M%3UjH-KLC4dipM9oII5y6e9IU$=Sz + @@ -213,6 +214,7 @@ + @@ -224,6 +226,7 @@ + diff --git a/TestEGr.vcxproj.filters b/TestEGr.vcxproj.filters index ddf2e83..6277e41 100644 --- a/TestEGr.vcxproj.filters +++ b/TestEGr.vcxproj.filters @@ -30,6 +30,9 @@ File di risorse + + File di risorse + @@ -62,6 +65,9 @@ File di intestazione + + File di intestazione + @@ -76,6 +82,9 @@ File di origine + + File di origine + diff --git a/TestEGrDlg.cpp b/TestEGrDlg.cpp index 3e5acad..f75f578 100644 --- a/TestEGrDlg.cpp +++ b/TestEGrDlg.cpp @@ -15,12 +15,18 @@ #include "stdafx.h" #include "TestEGr.h" #include "TestEGrDlg.h" +#include "TestEGrUtils.h" #include "resource.h" #include "/EgtDev/Include/EgtIniFile.h" #include "/EgtDev/Include/EgtILogger.h" +#include "/EgtDev/Include/EgtPerfCounter.h" +#include "/EgtDev/Include/EgtPointerOwner.h" +#include "/EgtDev/Include/EgnCmdParser.h" #include "/EgtDev/Include/EGkDllMain.h" #include "/EgtDev/Include/EGkGeomDB.h" -#include "/EgtDev/Include/EgtPerfCounter.h" +#include "/EgtDev/Include/EGkGdbExecutor.h" +#include "/EgtDev/Include/EGrScene.h" +#include "/EgtDev/Include/EGrSceExecutor.h" using namespace std ; @@ -32,6 +38,7 @@ using namespace std ; //---------------------------------------------------------------------------- // CAboutDlg dialog used for App About +//---------------------------------------------------------------------------- class CAboutDlg : public CDialog { public : @@ -56,6 +63,12 @@ CAboutDlg::OnInitDialog( void) GetDlgItem( IDC_EXEVER)->SetWindowText( stringtoW( GetExeNameVer())) ; + string sInfo ; + GetSceneInfo( sInfo) ; + ReplaceString( sInfo, "\n", "\r\n") ; + sInfo += "\r\n" ; + GetDlgItem( IDC_EXEINFO)->SetWindowText( stringtoW( sInfo)) ; + return TRUE ; } @@ -67,16 +80,11 @@ END_MESSAGE_MAP() // CTestEGrDlg dialog //---------------------------------------------------------------------------- CTestEGrDlg::CTestEGrDlg( ILogger* pLogger, CWnd* pParent) - : CDialog( IDD_TESTEGR_DIALOG, pParent) + :CDialog( IDD_TESTEGR_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME) ; + // memorizzo il puntatore al logger m_pLogger = pLogger ; - SetEGkLogger( m_pLogger) ; - m_pGeomDB = CreateGeomDB() ; - if ( m_pGeomDB != nullptr) - m_pGeomDB->Init() ; - else - LOG_ERROR( m_pLogger, "Error in CreateGeomDB") } //---------------------------------------------------------------------------- @@ -94,7 +102,9 @@ BEGIN_MESSAGE_MAP( CTestEGrDlg, CDialog) ON_WM_QUERYDRAGICON() ON_WM_SIZE() ON_WM_CLOSE() + ON_BN_CLICKED( IDC_NEW, OnFileNew) ON_BN_CLICKED( IDC_OPEN, OnFileOpen) + ON_BN_CLICKED( IDC_EXEC, OnFileExec) ON_CONTROL_RANGE( BN_CLICKED, IDC_ZOOM_ALL, IDC_ZOOM_M, OnZoom) ON_CONTROL_RANGE( BN_CLICKED, IDC_VIEW_TOP, IDC_VIEW_ISO, OnView) ON_BN_CLICKED( IDC_CLOSE, OnClose) @@ -142,6 +152,13 @@ CTestEGrDlg::OnInitDialog( void) m_nSideBarW = rectClient.right - rectView.right ; m_nBottomBarH = rectClient.bottom - rectView.bottom ; + // creo il DB geometrico + m_pGeomDB = CreateGeomDB() ; + if ( m_pGeomDB != nullptr) + m_pGeomDB->Init() ; + else + LOG_ERROR( m_pLogger, "Error in CreateGeomDB") + // recupero i parametri della scena int nDriver = GetPrivateProfileInt( "OpenGL", "Driver", 2, AfxGetApp()->m_pszProfileName) ; bool b2Buff = ( GetPrivateProfileInt( "OpenGL", "DoubleBuffer", 1, AfxGetApp()->m_pszProfileName) == 1) ; @@ -149,8 +166,14 @@ CTestEGrDlg::OnInitDialog( void) int nDepthBits = GetPrivateProfileInt( "OpenGL", "DepthBits", 16, AfxGetApp()->m_pszProfileName) ; // creazione della scena - m_View.Create( this, IDC_SCENE) ; - m_View.StartScene( nDriver, b2Buff, nColorBits, nDepthBits, m_pGeomDB, m_pLogger) ; + if ( m_View.Create( this, IDC_SCENE) && + m_View.StartScene( nDriver, b2Buff, nColorBits, nDepthBits)) + m_View.GetScene()->Init( m_pGeomDB) ; + + // log con info sulla scena + string sSceneInfo ; + m_View.GetSceneInfo( sSceneInfo) ; + LOG_INFO( m_pLogger, sSceneInfo.c_str()) // recupero il posizionamento della finestra del dialogo string sTmp = GetPrivateProfileStringUtf8( "General", "WinPlace", "", AfxGetApp()->m_pszProfileName) ; @@ -238,7 +261,9 @@ CTestEGrDlg::OnSize( UINT nType, int cx, int cy) // se cambiata dimensione if ( nType == SIZE_RESTORED || nType == SIZE_MAXIMIZED) { // spostamento bottoni + MoveDlgItem( IDC_NEW, IP_TR, cx, cy) ; MoveDlgItem( IDC_OPEN, IP_TR, cx, cy) ; + MoveDlgItem( IDC_EXEC, IP_TR, cx, cy) ; MoveDlgItem( IDC_SEPAR, IP_TR, cx, cy) ; MoveDlgItem( IDC_ZOOM_ALL, IP_TR, cx, cy) ; MoveDlgItem( IDC_ZOOM_P, IP_TR, cx, cy) ; @@ -331,6 +356,18 @@ CTestEGrDlg::ReshapeDlgItem( int nID, int nFlag, int nW, int nH) return ( pBtn->SetWindowPos( NULL, 0, 0, nWu, nHu, SWP_NOMOVE | SWP_NOZORDER) != 0) ; } +//---------------------------------------------------------------------------- +void +CTestEGrDlg::OutInfo( const string& sOut, bool bLog) +{ + CWnd* pWnd = GetDlgItem( IDC_INFO) ; + if ( pWnd != nullptr) + pWnd->SetWindowText( stringtoW( sOut)) ; + + if ( bLog) + LOG_INFO( m_pLogger, sOut.c_str()) +} + //---------------------------------------------------------------------------- void CTestEGrDlg::OnClose( void) @@ -352,6 +389,17 @@ CTestEGrDlg::OnClose( void) EndDialog( IDCLOSE) ; } +//---------------------------------------------------------------------------- +void +CTestEGrDlg::OnFileNew( void) +{ + // pulizia e reinizializzazione del DB geometrico + m_pGeomDB->Clear() ; + m_pGeomDB->Init() ; + // visualizzo con zoom all + m_View.Zoom( IDC_ZOOM_ALL) ; +} + //---------------------------------------------------------------------------- void CTestEGrDlg::OnFileOpen( void) @@ -403,15 +451,75 @@ CTestEGrDlg::OnFileOpen( void) m_View.Zoom( IDC_ZOOM_ALL) ; Counter.Stop() ; string sOut = "First ZoomAll time = " + ToString( Counter.GetTime(), 2) + " ms" ; - // eseguo ridisegno - Counter.Start() ; - m_View.Redraw() ; - Counter.Stop() ; - sOut += " Redraw time = " + ToString( Counter.GetTime(), 2) + " ms" ; // emetto info - GetDlgItem( IDC_INFO)->SetWindowText( stringtoW( sOut)) ; - LOG_INFO( m_pLogger, sOut.c_str()) + ::OutInfo( sOut) ; +} +//---------------------------------------------------------------------------- +void +CTestEGrDlg::OnFileExec( void) +{ + PtrOwner pGdbExec( CreateGdbExecutor()) ; + PtrOwner pSceExec( CreateSceExecutor()) ; + PtrOwner pCmdParser( CreateCmdParser()) ; + + // controllo validità oggetti + if ( ! IsValid( pGdbExec)) { + AfxMessageBox( L"Error in CreateGdbExecutor", MB_ICONSTOP|MB_OK) ; + return ; + } + if ( ! IsValid( pSceExec)) { + AfxMessageBox( L"Error in CreateSceExecutor", MB_ICONSTOP|MB_OK) ; + return ; + } + if ( ! IsValid( pCmdParser)) { + AfxMessageBox( L"Error in CreateCmdParser", MB_ICONSTOP|MB_OK) ; + return ; + } + // verifico validità DB geometrico + if ( m_pGeomDB == nullptr) { + AfxMessageBox( L"Error in CreateGeomDB", MB_ICONSTOP|MB_OK) ; + return ; + } + + // visualizzo il dialogo di scelta file + CFileDialog dlg( TRUE, L"*.Tsc", NULL, + OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_NOCHANGEDIR, + L"Test script EgalTech (*.Tsc) |*.Tsc|", NULL, 0, FALSE) ; + + // imposto il direttorio iniziale + wstring wsDir = stringtoW( GetPrivateProfileStringUtf8( "General", "LastTscDir", "", AfxGetApp()->m_pszProfileName)) ; + if ( ! wsDir.empty()) + dlg.m_ofn.lpstrInitialDir = wsDir.c_str() ; + + // lancio il dialogo di scelta file + if ( dlg.DoModal() != IDOK) + return ; + + // recupero la path del file e ne salvo il direttorio + string sFilePath = WtoA( dlg.GetPathName()) ; + if ( sFilePath.empty()) + return ; + string sFileDir ; + string sFileName ; + SplitLast( sFilePath, "\\", sFileDir, sFileName) ; + if ( ! sFileDir.empty()) + WritePrivateProfileStringUtf8( "General", "LastTscDir", sFileDir.c_str(), AfxGetApp()->m_pszProfileName) ; + + // esecuzione script + pGdbExec->Init( m_pGeomDB) ; + pSceExec->Init( m_View.GetScene()) ; + pGdbExec->AddExecutor( Get( pSceExec)) ; + if ( ! pCmdParser->Init( sFilePath, Get( pGdbExec), m_pLogger, 0)) { + string sOut = "Error on Parser.Init (" + sFilePath + ")" ; + AfxMessageBox( stringtoW( sOut), MB_ICONSTOP|MB_OK) ; + return ; + } + if ( ! pCmdParser->Run()) { + string sOut = "Error on Parser.Run (look at Log file)" ; + AfxMessageBox( stringtoW( sOut), MB_ICONSTOP|MB_OK) ; + return ; + } } //---------------------------------------------------------------------------- diff --git a/TestEGrDlg.h b/TestEGrDlg.h index c468adc..26649ed 100644 --- a/TestEGrDlg.h +++ b/TestEGrDlg.h @@ -25,6 +25,9 @@ class CTestEGrDlg : public CDialog public : CTestEGrDlg( ILogger* pLogger = nullptr, CWnd* pParent = nullptr) ; ~CTestEGrDlg( void) ; + void OutInfo( const std::string& sOut, bool bLog = true) ; + bool GetSceneInfo( std::string& sInfo) + { return m_View.GetSceneInfo( sInfo) ; } protected : virtual BOOL OnInitDialog( void) ; @@ -33,7 +36,9 @@ class CTestEGrDlg : public CDialog afx_msg void OnPaint( void) ; afx_msg HCURSOR OnQueryDragIcon( void) ; afx_msg void OnSize( UINT nType, int cx, int cy) ; + afx_msg void OnFileNew( void) ; afx_msg void OnFileOpen( void) ; + afx_msg void OnFileExec( void) ; afx_msg void OnZoom( UINT nID) ; afx_msg void OnView( UINT nID) ; afx_msg void OnClose( void) ; @@ -47,10 +52,10 @@ class CTestEGrDlg : public CDialog bool ReshapeDlgItem( int nID, int nFlag, int nW, int nH) ; private : - HICON m_hIcon ; - TestEgrView m_View ; - ILogger* m_pLogger ; IGeomDB* m_pGeomDB ; + ILogger* m_pLogger ; + TestEgrView m_View ; + HICON m_hIcon ; int m_nPrevCx ; int m_nPrevCy ; int m_nSideBarW ; diff --git a/TestEGrUtils.cpp b/TestEGrUtils.cpp new file mode 100644 index 0000000..5928435 --- /dev/null +++ b/TestEGrUtils.cpp @@ -0,0 +1,39 @@ +//---------------------------------------------------------------------------- +// EgalTech 2014-2014 +//---------------------------------------------------------------------------- +// File : TestEGrUtils.cpp Data : 17.02.14 Versione : 1.5b3 +// Contenuto : Implementazione funzioni di utilità +// +// +// +// Modifiche : 17.02.14 DS Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "TestEGrUtils.h" +#include "TestEGrDlg.h" + +using namespace std ; + +//---------------------------------------------------------------------------- +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +//---------------------------------------------------------------------------- +void +OutInfo( string& sOut, bool bLog) +{ + ((CTestEGrDlg*)AfxGetMainWnd())->OutInfo( sOut, bLog) ; +} + +//---------------------------------------------------------------------------- +bool +GetSceneInfo( string& sInfo) +{ + return ((CTestEGrDlg*)AfxGetMainWnd())->GetSceneInfo( sInfo) ; +} diff --git a/TestEGrUtils.h b/TestEGrUtils.h new file mode 100644 index 0000000..855f5b9 --- /dev/null +++ b/TestEGrUtils.h @@ -0,0 +1,20 @@ +//---------------------------------------------------------------------------- +// EgalTech 2014-2014 +//---------------------------------------------------------------------------- +// File : TestEGrUtils.h Data : 17.02.14 Versione : 1.5b3 +// Contenuto : Dichiarazione funzioni di utilità. +// +// +// +// Modifiche : 17.02.14 DS Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#pragma once + +#include + +//---------------------------------------------------------------------------- +void OutInfo( std::string& sOut, bool bLog = true) ; +bool GetSceneInfo( std::string& sInfo) ; diff --git a/TestEGrView.h b/TestEGrView.h index 6420f61..e111797 100644 --- a/TestEGrView.h +++ b/TestEGrView.h @@ -4,6 +4,7 @@ #include "/EgtDev/Include/EGkPoint3d.h" #include +#include class IGeomDB ; class ILogger ; @@ -16,8 +17,10 @@ class TestEgrView : public CWnd TestEgrView( void) ; ~TestEgrView( void) ; bool Create( CWnd* pParent, int nID) ; - bool StartScene( int nDriver, bool b2Buff, int nColorBits, int nDepthBits, - IGeomDB* pGeomDB, ILogger* pLogger) ; + bool StartScene( int nDriver, bool b2Buff, int nColorBits, int nDepthBits) ; + IEGrScene* GetScene( void) + { return m_pScene ; } + bool GetSceneInfo( std::string& sInfo) ; void Destroy( void) ; bool Reshape( int nX, int nY, int nW, int nH) ; void Redraw( void) ; @@ -26,6 +29,8 @@ class TestEgrView : public CWnd protected : afx_msg void OnPaint( void) ; + afx_msg BOOL OnEraseBkgnd( CDC* pDC) + { return TRUE ; } afx_msg void OnMButtonDown( UINT nFlags, CPoint point) ; afx_msg void OnMButtonUp( UINT nFlags, CPoint point) ; afx_msg void OnMouseMove( UINT nFlags, CPoint point) ; diff --git a/TestEgrView.cpp b/TestEgrView.cpp index f4fc116..4151d9e 100644 --- a/TestEgrView.cpp +++ b/TestEgrView.cpp @@ -14,12 +14,16 @@ //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "TestEgrView.h" +#include "TestEGrUtils.h" #include "resource.h" +#include "/EgtDev/Include/EgtPerfCounter.h" #include "/EgtDev/Include/EgtILogger.h" #include "/EgtDev/Include/EgnStringUtils.h" #include "/EgtDev/Include/EgnStringConverter.h" #include "/EgtDev/Include/EGrScene.h" +using namespace std ; + //---------------------------------------------------------------------------- #ifdef _DEBUG #define new DEBUG_NEW @@ -83,14 +87,12 @@ TestEgrView::Create( CWnd* pParent, int nID) //---------------------------------------------------------------------------- bool -TestEgrView::StartScene( int nDriver, bool b2Buff, int nColorBits, int nDepthBits, - IGeomDB* pGeomDB, ILogger* pLogger) +TestEgrView::StartScene( int nDriver, bool b2Buff, int nColorBits, int nDepthBits) { if ( m_pScene == nullptr) return false ; // creazione della scena - m_pScene->Init( pGeomDB, pLogger) ; if ( ! m_pScene->CreateContext( GetSafeHdc(), nDriver, b2Buff, nColorBits, nDepthBits)) return false ; @@ -98,9 +100,19 @@ TestEgrView::StartScene( int nDriver, bool b2Buff, int nColorBits, int nDepthBit m_pScene->SetCamera( CT_ISO_SW) ; m_pScene->ZoomAll() ; - LOG_INFO( pLogger, m_pScene->GetOpenGLInfo().c_str()) - LOG_INFO( pLogger, m_pScene->GetGLSLInfo().c_str()) - LOG_INFO( pLogger, m_pScene->GetPixelFormatInfo().c_str()) + return true ; +} + +//---------------------------------------------------------------------------- +bool +TestEgrView::GetSceneInfo( string& sInfo) +{ + if ( m_pScene == nullptr) + return false ; + + sInfo = m_pScene->GetOpenGLInfo() + '\n' + + m_pScene->GetGLSLInfo() + '\n' + + m_pScene->GetPixelFormatInfo() ; return true ; } @@ -134,9 +146,18 @@ TestEgrView::OnPaint( void) if ( m_pScene == nullptr) return ; + PerformanceCounter Counter ; + // ridisegno finestra OpenGL + Counter.Start() ; m_pScene->Draw() ; ValidateRgn( NULL) ; + Counter.Stop() ; + + // emetto info + string sOut = "Redraw time = " + ToString( Counter.GetTime(), 2) + " ms" ; + ::OutInfo( sOut, false) ; + } //---------------------------------------------------------------------------- diff --git a/res/Logo.bmp b/res/Logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c827a644de3c53910906ce24bba12a20549dcc7f GIT binary patch literal 6966 zcmd5u=Z8 z^d>&FmFIxx>qmG0Z^z%p&wVy16NcGw98oaGq3w|4etb66U4#}Q98N;4TAJig90gwJ zB~Fln0%pZgp^zZULV=l<5t4-~2vQs}>zo!M57HTlNEU&|t`J0=#wsGhoi73dPpL(x zkerT4DNDhYZ0MV5(sZ{aC@C^2B|(<>P<(+Z>>y0xoH-P9TrEZhUghziLXn{$5ym=; z(+^m#DhB-$W?2}RJh=Jp=}N#ke7$}J*{>g7hV1QyX>`1Lr1@ywh95Ly zmCjFYTu|Rzr}ha)fT6o$YHjhj_#q%6fSn;)tKIUkJ&GSMokJBaT!;dIv;+>pj{Zo3 z*Fv`JNRcoz@`GUQ;vu>9$#ikXgZ$_My%%GjDJI&^o}bYI=B7<_Rc&cg|aA6`Bj%#h$@K1IDdIgTJM7m4ej zik4W8vu6>*3C^BXOfrX9TA?-Z0G57SP*s{G&$<)G6^k&YAJ{7wvji1{SrQ%s84yl{ z?-8F35*%UNdNpKa@bO@wPR`0e8RRY2@`EnrYAj`2V9OryyqM9n;|E#n5fECkr45jI zw6etUdWC-eF`IE9<=YDUG41tfkFL#^O=xHxA%A&=e-@i0e zkahBsCOMdNKM=_}sN&4D_8HL9 zrO6EQgW=MNy-ejZXU1{rmzF@zOd`52@6MDXiv7}pf-cZ|l`+&f+wv^hp#4Q1-?_RR z33dWE3@LrWsU)vt&I1XI%QLPH%Uk~!OK?yCW$3^I<^*d7aoG#tNIzbfxAyq1T&EKc z@-u$-z{I`oc<|I9KfWqF4&5=#Sv(=VZz~?;2Md7w+^)1F8umrYK)?t2L2Z5rPytKp z`JL0*6%cLA(7GHB^@E+8Eoph+|Hohnsn(~?MEpR6JD2wYHzKsdx2v`xUQeB^Jt;q> M&ZNLFz5m1TA8q=szW@LL literal 0 HcmV?d00001 diff --git a/resource.h b/resource.h index 31cdb2878bcf06a43513a44e74c626e9d68a07f4..b907b91c9d1aa8bd58076669900facb3249d28de 100644 GIT binary patch delta 170 zcmX>k-X*gD-<4LjZ%}h1rn7bh06n>|`lUm&su)a*WPkJ@=T{ zCOdFTfb>nyV_{?WWbk8f1DmG}(gl>`*c`{H!N_PlS&>nE@;t63j21v+M1d@k$vNC% E0EgZzB>(^b delta 45 zcmeB@J|w