// DenAgent.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "DenAgent.h" #include "DenAgentDlg.h" #include "TrayWnd.h" #include #include "DenAgent_i.c" #include "..\Inject\Inject.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //FILELIST g_FileList[] = { { "messages.xml", "messages.dlc" }, { "memlocs.xml", "messages.dlc" }, { "decalplugins.xml", "messages.dlc" } }; ///////////////////////////////////////////////////////////////////////////// // CDenAgentApp BEGIN_MESSAGE_MAP(CDenAgentApp, CWinApp) //{{AFX_MSG_MAP(CDenAgentApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG ON_COMMAND(ID_HELP, CWinApp::OnHelp) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDenAgentApp construction CDenAgentApp::CDenAgentApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } ///////////////////////////////////////////////////////////////////////////// // The one and only CDenAgentApp object CDenAgentApp theApp; LONG g_fAbortDownload; ///////////////////////////////////////////////////////////////////////////// // CDenAgentApp initialization bool CheckForHardwareMode () { RegKey key; if (key.Open (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00", KEY_READ) != ERROR_SUCCESS) { return false; } DWORD dwValue = 0; if (key.QueryDWORDValue ("UseHardware", dwValue) != ERROR_SUCCESS) { return false; } return dwValue ? true : false; } bool CheckForIE5OrLater () { RegKey key; if (key.Open (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Internet Explorer", KEY_READ) != ERROR_SUCCESS) return false; DWORD dwValue = 0; TCHAR szVersionBuffer[256]; DWORD dwSize = sizeof (szVersionBuffer); if (key.QueryStringValue("Version", szVersionBuffer, &dwSize) != ERROR_SUCCESS) return false; DWORD dwMajor = 0, dwMinor = 0, dwBuild1 = 0, dwBuild2 = 0; if (::_stscanf (szVersionBuffer, _T("%ld.%ld.%ld.%ld"), &dwMajor, &dwMinor, &dwBuild1, &dwBuild2) != 4) return false; // IE 5.01 is build: 5.00.2919.6307 return !( dwMajor < 5 || ( dwMajor == 5 && dwMinor == 0 ) && ( ( dwBuild1 < 2919 ) || ( dwBuild1 == 2919 && dwBuild2 < 6307 ) ) ); } bool CDenAgentApp::getVersionString ( LPCTSTR szFilename, CString &strVersion ) { DWORD dwDummy, dwVerSize = ::GetFileVersionInfoSize( const_cast< LPTSTR > ( szFilename ), &dwDummy ); if( dwVerSize == 0 ) return false; BYTE *pbVersionInfo = reinterpret_cast< BYTE * >( ::_alloca( dwVerSize ) ); ::GetFileVersionInfo( const_cast< LPTSTR > ( szFilename ), 0, dwVerSize, pbVersionInfo ); VS_FIXEDFILEINFO *vffi; UINT nLength = sizeof( VS_FIXEDFILEINFO ); if( !::VerQueryValue( pbVersionInfo, _T( "\\" ), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) ) return false; // Got it, so format it strVersion.FormatMessage ( IDS_VERSIONTEMPLATE, static_cast< int > ( HIWORD ( vffi->dwFileVersionMS ) ), static_cast< int > ( LOWORD ( vffi->dwFileVersionMS ) ), static_cast< int > ( HIWORD ( vffi->dwFileVersionLS ) ), static_cast< int > ( LOWORD ( vffi->dwFileVersionLS ) ) ); return true; } bool CDenAgentApp::getVersionInfo ( LPCTSTR szFilename, int &iReleaseMajor, int &iReleaseMinor, int &iBuildMajor, int &iBuildMinor ) { DWORD dwDummy, dwVerSize = ::GetFileVersionInfoSize( const_cast< LPTSTR > ( szFilename ), &dwDummy ); if( dwVerSize == 0 ) return false; BYTE *pbVersionInfo = reinterpret_cast< BYTE * >( ::_alloca( dwVerSize ) ); ::GetFileVersionInfo( const_cast< LPTSTR > ( szFilename ), 0, dwVerSize, pbVersionInfo ); VS_FIXEDFILEINFO *vffi; UINT nLength = sizeof( VS_FIXEDFILEINFO ); if( !::VerQueryValue( pbVersionInfo, _T( "\\" ), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) ) return false; // Got it, so format it iReleaseMajor = static_cast< int > ( HIWORD ( vffi->dwFileVersionMS ) ); iReleaseMinor = static_cast< int > ( LOWORD ( vffi->dwFileVersionMS ) ); iBuildMajor = static_cast< int > ( HIWORD ( vffi->dwFileVersionLS ) ); iBuildMinor = static_cast< int > ( LOWORD ( vffi->dwFileVersionLS ) ); return true; } bool CDenAgentApp::getACVersionString ( CString &strVersion ) { RegKey rk; if ( rk.Open ( HKEY_LOCAL_MACHINE, _T( "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ) ) != ERROR_SUCCESS ) { ::AfxMessageBox ( IDE_NOCLIENTEXE, MB_ICONWARNING ); return false; } TCHAR szClientPath[ MAX_PATH ]; DWORD dwPathLength = MAX_PATH; if ( rk.QueryStringValue ( _T( "path" ), szClientPath, &dwPathLength ) != ERROR_SUCCESS ) { ::AfxMessageBox ( IDE_NOCLIENTEXE, MB_ICONWARNING ); return false; } ::_tcscpy ( szClientPath + ( dwPathLength - 1 ), _T( "\\client.exe" ) ); bool bHasVersion = getVersionString ( szClientPath, strVersion ); if ( !bHasVersion ) ::AfxMessageBox ( IDE_NOCLIENTVER, MB_ICONWARNING ); return bHasVersion; } bool CDenAgentApp::getCOMObjectDLL ( REFCLSID rclsid, CString &strFilename ) { USES_CONVERSION; LPOLESTR oclsid; HRESULT hRes = ::StringFromCLSID ( rclsid, &oclsid ); _ASSERTE ( SUCCEEDED ( hRes ) ); LPCTSTR clsidName = OLE2T ( oclsid ); ::CoTaskMemFree ( oclsid ); TCHAR keyName[ MAX_PATH ]; ::_stprintf ( keyName, _T( "CLSID\\%s\\InprocServer32" ), clsidName ); RegKey rk; if ( rk.Open ( HKEY_CLASSES_ROOT, keyName ) != ERROR_SUCCESS ) return false; DWORD dwPathSize = MAX_PATH; long regResult = rk.QueryStringValue ( NULL, strFilename.GetBuffer ( MAX_PATH ), &dwPathSize ); strFilename.ReleaseBuffer(); if (regResult != ERROR_SUCCESS) return false; // Check to see if the default KeyValue points to the MS.NET core DLL const char *dotNetProxy = "mscoree.dll"; CString dotNetMatchString = strFilename.Right(strlen(dotNetProxy)); if (dotNetMatchString.CompareNoCase(dotNetProxy) == 0) { // Get CodeBase KeyValue - Should point to the Plugin DLL dwPathSize = MAX_PATH; if (rk.QueryStringValue ( "CodeBase", strFilename.GetBuffer ( MAX_PATH ), &dwPathSize ) == ERROR_SUCCESS) { // Release extra buffer (Not doing this seem to cause the Left() and Delete() funcs to fail) strFilename.ReleaseBuffer(); // Check for garbage in KeyValue and delete it if (strFilename.Left(8) == "file:///") strFilename.Delete(0,8); } else { // CodeBase isn't accessable - Possible that plugin is in GAC // Return Path to MS.NET core DLL dwPathSize = MAX_PATH; if (rk.QueryStringValue ( NULL, strFilename.GetBuffer ( MAX_PATH ), &dwPathSize ) != ERROR_SUCCESS) { rk.Close(); strFilename.ReleaseBuffer(); return false; } } } rk.Close(); return true; } bool CDenAgentApp::getAgentPath ( LPCTSTR szFilename, CString &strPath ) { TCHAR *szAgentPath = strPath.GetBuffer ( MAX_PATH ); ::GetModuleFileName ( NULL, szAgentPath, MAX_PATH ); TCHAR *filePath = ::_tcsrchr ( szAgentPath, _T( '\\' ) ); ::strcpy ( filePath + 1, szFilename ); return true; } bool CDenAgentApp::checkXMLVersion ( CString &strClientVersion, CString &strXMLFile, CTrayWnd* pTrayWnd ) { MSXML::IXMLDOMDocumentPtr pDoc; try { pDoc.CreateInstance ( __uuidof ( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER ); pDoc->async = false; BOOL bSuccess = pDoc->load( static_cast< LPCTSTR > ( strXMLFile ) ); if( !bSuccess ) { std::string szXML; DecryptXML( static_cast< LPCSTR >( strXMLFile ), szXML ); if( szXML != "" ) bSuccess = pDoc->loadXML( _bstr_t( szXML.c_str() ) ); } if( ! bSuccess ) { CString strMessage; strMessage.FormatMessage ( IDE_NOXMLDOC, static_cast< LPCTSTR > ( strXMLFile ) ); CString strUpdate; strUpdate.LoadString( IDE_UPDATETEXT ); strMessage += strUpdate; if ( ::AfxMessageBox ( strMessage, MB_YESNO | MB_ICONERROR ) == IDYES ) pTrayWnd->UpdateXMLFiles( ); return false; } // Read the client version string MSXML::IXMLDOMNodePtr pVersionNode = pDoc->selectSingleNode ( _T( "/*/@version" ) ); if ( pVersionNode == NULL ) { CString strError; strError.FormatMessage ( IDE_NOXMLVER, static_cast< LPCTSTR > ( strXMLFile ) ); ::AfxMessageBox ( strError, MB_ICONWARNING ); return false; } _variant_t vXMLVer = pVersionNode->text; if ( vXMLVer.vt != VT_BSTR ) { CString strError; strError.FormatMessage ( IDE_NOXMLVER, static_cast< LPCTSTR > ( strXMLFile ) ); ::AfxMessageBox ( strError, MB_ICONWARNING ); return false; } CString strXMLVer ( vXMLVer.bstrVal ); if ( strClientVersion.Compare ( strXMLVer ) != 0 ) { CString str; str.FormatMessage ( IDE_XMLCLIENTVERSIONMISMATCH, static_cast< LPCTSTR > ( strXMLFile ), static_cast< LPCTSTR > ( strClientVersion ), static_cast< LPCTSTR > ( strXMLVer ) ); CString strUpdate; strUpdate.LoadString( IDE_UPDATETEXT ); str += strUpdate; if ( ::AfxMessageBox ( str, MB_YESNO | MB_ICONWARNING ) == IDYES ) pTrayWnd->UpdateXMLFiles( ); return false; } return true; } catch ( _com_error & ) { CString strMessage; strMessage.FormatMessage ( IDE_NOXMLDOC, static_cast< LPCTSTR > ( strXMLFile ) ); ::AfxMessageBox ( strMessage, MB_ICONERROR ); } return true; } void CDenAgentApp::DecryptXML( const char *szPath, std::string &szXML ) { if( szPath == NULL ) { szXML = ""; return; } FILE *f = fopen( szPath, "rb" ); if( f == NULL ) { szXML = ""; return; } szXML.clear(); unsigned char szBuffer[1025]; try { CCryptProv crypt; if( crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV ) == NTE_BAD_KEYSET ) crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV, CRYPT_NEWKEYSET ); CCryptMD5Hash hash; hash.Initialize( crypt ); hash.AddString( DECAL_KEY ); CCryptDerivedKey key; key.Initialize( crypt, hash ); DWORD dwDecLen = 0; while( ! feof(f) ) { memset( szBuffer, 0, sizeof( szBuffer ) ); dwDecLen = fread( szBuffer, 1, 1024, f ); key.Decrypt( feof(f), (BYTE *) szBuffer, &dwDecLen ); szXML += (char *)szBuffer; } key.Destroy(); hash.Destroy(); crypt.Release(); } catch( ... ) { // crap... szXML = ""; } fclose( f ); } bool CDenAgentApp::checkXMLVersions ( CTrayWnd* pTrayWnd ) { bool bOK = true; // Check the versions of the XML files CString strClientVersion; if ( getACVersionString ( strClientVersion ) ) { CString strXMLFile; if ( getAgentPath ( _T( "memlocs.xml" ), strXMLFile ) ) bOK = bOK && checkXMLVersion ( strClientVersion, strXMLFile, pTrayWnd ); //if ( getAgentPath ( _T( "messages.xml" ), strXMLFile ) ) //bOK = bOK && checkXMLVersion ( strClientVersion, strXMLFile ); } return bOK; } void CDenAgentApp::getXMLBuilds ( cXMLBuild *pFirst, cXMLBuild *pLast ) { MSXML::IXMLDOMDocumentPtr pDoc; pDoc.CreateInstance ( __uuidof ( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER ); pDoc->async = false; for ( ; pFirst != pLast; ++ pFirst ) { CString strPath; getAgentPath ( pFirst->XMLFile, strPath ); try { BOOL bSuccess = pDoc->load( static_cast< LPCTSTR > ( pFirst->XMLFile ) ); if( ! bSuccess ) { std::string szXML; DecryptXML( static_cast< LPCSTR >( pFirst->XMLFile ), szXML ); if( szXML != "" ) bSuccess = pDoc->loadXML( _bstr_t( szXML.c_str() ) ); } if( ! bSuccess ) { CString strMessage; strMessage.FormatMessage( IDE_NOXMLDOC, pFirst->XMLFile ); ::AfxMessageBox ( strMessage, MB_ICONERROR ); pFirst->build = 0; continue; } // Read the client version string MSXML::IXMLDOMNodePtr pBuildNode = pDoc->selectSingleNode ( _T( "/*/@build" ) ); if ( pBuildNode == NULL ) { pFirst->build = 0; continue; } pFirst->build = pBuildNode->GetnodeValue (); } catch ( _com_error & ) { pFirst->build = 0; } } } BOOL CDenAgentApp::InitInstance() { HWND hDenAgent = FindWindow(NULL, "Decal Agent"); if(hDenAgent) { ::MessageBox(hDenAgent, "Decal is already running!", "Decal", 0); return FALSE; } if (!CheckForHardwareMode ()) { ::MessageBox ( NULL, _T("Decal requires Asheron's Call to be installed with hardware 3d acceleration enabled! Decal will run, but will be unable to display any UI in-game."), _T("Decal Agent Startup Warning"), MB_OK ); } if (!CheckForIE5OrLater ()) { ::MessageBox ( NULL, "Decal requires Internet Explorer 5.01 or later!", "Decal Agent Startup Error", MB_OK ); return FALSE; } if (!InitATL()) return FALSE; CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated) { return TRUE; } // Set our current directory to the path of DenAgent.exe // TODO: Fix all the code so that it doesn't rely on the current directory TCHAR szAppPath[MAX_PATH]; ::memset (szAppPath, 0, sizeof (szAppPath)); ::GetModuleFileName (0, szAppPath, MAX_PATH); TCHAR *szLastSlash = ::_tcsrchr (szAppPath, _T('\\')); if (szLastSlash) { *szLastSlash = 0; ::SetCurrentDirectory (szAppPath); } // Move V1 plugins into the V2 registry format importV1Plugins (); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. m_pTrayWnd = new CTrayWnd; m_pMainWnd = m_pTrayWnd; m_pTrayWnd->CreateEx( 0, ::AfxRegisterWndClass( 0, NULL, NULL, NULL ), _T( "Decal Agent" ), WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL ); checkXMLVersions ( m_pTrayWnd ); return TRUE; } CDenAgentModule _Module; BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP() LONG CDenAgentModule::Unlock() { AfxOleUnlockApp(); return 0; } LONG CDenAgentModule::Lock() { AfxOleLockApp(); return 1; } LPCTSTR CDenAgentModule::FindOneOf(LPCTSTR p1, LPCTSTR p2) { while (*p1 != NULL) { LPCTSTR p = p2; while (*p != NULL) { if (*p1 == *p) return CharNext(p1); p = CharNext(p); } p1++; } return NULL; } int CDenAgentApp::ExitInstance() { delete m_pTrayWnd; if (m_bATLInited) { _Module.RevokeClassObjects(); _Module.Term(); CoUninitialize(); } return CWinApp::ExitInstance(); } void CDenAgentApp::importV1Plugins () { USES_CONVERSION; RegKey hkGroup; if ( hkGroup.Open ( HKEY_LOCAL_MACHINE, _T ( "Software\\Decal\\Plugins" ) ) != ERROR_SUCCESS ) // No plugins - no problem return; DWORD dwValues = 0; ::RegQueryInfoKey ( hkGroup, NULL, NULL, NULL, NULL, NULL, NULL, &dwValues, NULL, NULL, NULL, NULL ); // Now - iterate through the values backwards for ( int i = dwValues - 1; i >= 0; -- i ) { TCHAR szCLSID[ 64 ]; DWORD dwCLSID = sizeof ( szCLSID ); DWORD dwEnabled; DWORD dwEnabledSize = sizeof ( dwEnabled ); if ( ::RegEnumValue ( hkGroup, i, szCLSID, &dwCLSID, NULL, NULL, reinterpret_cast< BYTE * > ( &dwEnabled ), &dwEnabledSize ) != ERROR_SUCCESS ) continue; // Try and convert the name to a CLSID CLSID clsidPlugin; HRESULT hRes = ::CLSIDFromString ( T2OLE ( szCLSID ), &clsidPlugin ); if ( FAILED ( hRes ) ) continue; // Delete the value and ask questions later hkGroup.DeleteValue ( szCLSID ); // Try and create a key to extract the friendly name CComPtr< IPlugin > pPlugin; hRes = ::CoCreateInstance ( clsidPlugin, NULL, CLSCTX_INPROC_SERVER, __uuidof ( IPlugin ), reinterpret_cast< LPVOID * > ( &pPlugin ) ); if ( FAILED ( hRes ) ) // Bad plugin - skip continue; CComBSTR strName; pPlugin->get_FriendlyName ( &strName ); // Create the Real registry key // Key configuration copied from Decal/cActiveXSurrogate::Register RegKey hkeyItem; if ( hkeyItem.Create ( hkGroup, szCLSID ) != ERROR_SUCCESS ) continue; // Friendly name hkeyItem.SetStringValue( NULL, OLE2T( strName ) ); // Enabled by default hkeyItem.SetDWORDValue( _T( "Enabled" ), dwEnabled ); // The V1 surrogate hkeyItem.SetStringValue( _T( "Surrogate" ), _T( "{3D837F6E-B5CA-4604-885F-7AB45FCFA62A}" ) ); hkeyItem.Close(); } } BOOL CDenAgentApp::InitATL() { m_bATLInited = TRUE; HRESULT hRes = CoInitialize(NULL); if (FAILED(hRes)) { m_bATLInited = FALSE; return FALSE; } _Module.Init(ObjectMap, AfxGetInstanceHandle()); _Module.dwThreadID = GetCurrentThreadId(); LPTSTR lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT TCHAR szTokens[] = _T("-/"); BOOL bRun = TRUE; LPCTSTR lpszToken = _Module.FindOneOf(lpCmdLine, szTokens); while (lpszToken != NULL) { if (lstrcmpi(lpszToken, _T("UnregServer"))==0) { _Module.UpdateRegistryFromResource(IDR_DENAGENT, FALSE); _Module.UnregisterServer(TRUE); //TRUE means typelib is unreg'd bRun = FALSE; break; } if (lstrcmpi(lpszToken, _T("RegServer"))==0) { _Module.UpdateRegistryFromResource(IDR_DENAGENT, TRUE); _Module.RegisterServer(TRUE); bRun = FALSE; break; } lpszToken = _Module.FindOneOf(lpszToken, szTokens); } if (!bRun) { m_bATLInited = FALSE; _Module.Term(); CoUninitialize(); return FALSE; } hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE); if (FAILED(hRes)) { m_bATLInited = FALSE; CoUninitialize(); return FALSE; } return TRUE; }