Commits

Didrole committed 83888f3

Chat Logger++: Added "smart" sigscanning for IClientFriends functions, fixed wxWidgets 2.9 compatibility, added a single instance check, modified the trayicon to reflect the state of the logger.

Comments (0)

Files changed (13)

Projects/Chat Logger++/App.cpp

 */
 
 #include <wx/stdpaths.h>
+#include <wx/snglinst.h>
+
 #include "App.h"
 
 #include "TaskBarIcon.h"
 
 bool CApp::OnInit()
 {
+	static wxSingleInstanceChecker checker(wxString::Format(_T("Chat Logger++ - %s"), wxGetUserId()));
+	if(checker.IsAnotherRunning())
+	{
+		wxMessageBox(_("Chat Logger++ is already running."), _("Chat Logger++"), wxOK | wxCENTRE | wxICON_EXCLAMATION);
+		return false;
+	}
+
 	_set_invalid_parameter_handler(&InvalidParameterHandler);
 	setlocale(LC_TIME, "");
 
 	m_pTaskBarIcon = new CTaskBarIcon();
-	m_pTaskBarIcon->SetIcon(wxICON(AppIcon), L"Chat Logger++");
+	this->SetTrayIcon(k_EIconDisconnected, _("Not connected to Steam"));
 
 	m_pLogger = new CLogger();
 
 	}
 
 	wxMenu menu;
-	menu.Append(wxID_OPEN, L"Configuration");
+	menu.Append(wxID_OPEN, _("Configuration"));
 	menu.AppendSeparator();
-	menu.Append(wxID_AUTOSTART, L"Start with Windows", wxEmptyString, wxITEM_CHECK)->Check(bAutoRun);
+	menu.Append(wxID_AUTOSTART, _("Start with Windows"), wxEmptyString, wxITEM_CHECK)->Check(bAutoRun);
 	menu.AppendSeparator();
 	menu.Append(wxID_EXIT);
 
 		if(event.IsChecked())
 		{
 			wxString appPath = wxStandardPaths::Get().GetExecutablePath();
-			RegSetValueExW(hkRegistry, L"Chat Logger++", 0, REG_SZ, (BYTE*)appPath.c_str(), (wcslen(appPath.c_str()) + 1) * sizeof(wchar_t));
+			RegSetValueExW(hkRegistry, L"Chat Logger++", 0, REG_SZ, (BYTE*)appPath.wc_str(), (wcslen(appPath.wc_str()) + 1) * sizeof(wchar_t));
 		}
 		else
 		{
 	m_pTaskBarIcon->ShowMessage(_T(""), message, CTaskBarIcon::k_EIconWarning);
 }
 
+void CApp::SetTrayIcon(EIcon eIcon, const wxString& text/* = _T("")*/)
+{
+	wxString str;
+	if(text.Len())
+	{
+		str.Printf(_("Chat Logger++\n%s"), text);
+	}
+	else
+	{
+		str = _("Chat Logger++");
+	}
+
+	wxIcon icon;
+
+	switch(eIcon)
+	{
+	case k_EIconDisconnected:
+		{
+			icon = wxICON(3_AppDisconnectedIcon);
+			break;
+		}
+	case k_EIconWarning:
+		{
+			icon = wxICON(2_AppWarningIcon);
+			break;
+		}
+	case k_EIconNormal:
+	default:
+		{
+			icon = wxICON(1_AppIcon);
+			break;
+		}
+	}
+	
+	m_pTaskBarIcon->SetIcon(icon, str);
+}
+
 CLogger* CApp::GetLogger()
 {
 	return m_pLogger;

Projects/Chat Logger++/App.h

 	void Message(const wxString& message);
 	void Warning(const wxString& message);
 
+	enum EIcon
+	{
+		k_EIconNormal = 0,
+		k_EIconWarning,
+		k_EIconDisconnected,
+	};
+	void SetTrayIcon(EIcon eIcon, const wxString& text = _T(""));
+
 	CLogger* GetLogger();
 
 private:

Projects/Chat Logger++/Chat Logger++.vcproj

 				>
 			</File>
 			<File
+				RelativePath=".\SigScanner.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\TaskBarIcon.cpp"
 				>
 			</File>
 				>
 			</File>
 			<File
+				RelativePath=".\SigScanner.h"
+				>
+			</File>
+			<File
 				RelativePath=".\TaskBarIcon.h"
 				>
 			</File>
 				</FileConfiguration>
 			</File>
 			<File
+				RelativePath=".\app_disconnected.ico"
+				>
+			</File>
+			<File
+				RelativePath=".\app_error.ico"
+				>
+			</File>
+			<File
 				RelativePath=".\resource.rc"
 				>
 			</File>

Projects/Chat Logger++/ConfigurationDialog.cpp

 	EVT_BUTTON(wxID_ANY, CConfigurationDialog::OnButton)
 END_EVENT_TABLE()
 
-CConfigurationDialog::CConfigurationDialog() : wxDialog(NULL, wxID_ANY, wxString(_T("Chat Logger++ - Configuration")), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSYSTEM_MENU | wxCLOSE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT), m_config(_T("Chat Logger++"), _T("OSW"))
+CConfigurationDialog::CConfigurationDialog() : wxDialog(NULL, wxID_ANY, wxString(_("Chat Logger++ - Configuration")), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSYSTEM_MENU | wxCLOSE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT), m_config(_T("Chat Logger++"), _T("OSW"))
 {
 	m_bEnablePreviews = false;
 
 		underlinedAttr.SetFont(underlinedFont);
 
 		pTextCtrl->SetDefaultStyle(underlinedAttr);
-		pTextCtrl->AppendText(L"Common variables:\n\n");
+		pTextCtrl->AppendText(_("Common variables:\n\n"));
 		pTextCtrl->SetDefaultStyle(originalAttr);
 
 		pTextCtrl->AppendText
 		);
 
 		pTextCtrl->SetDefaultStyle(underlinedAttr);
-		pTextCtrl->AppendText(L"Message specific variables:\n\n");
+		pTextCtrl->AppendText(_("Message specific variables:\n\n"));
 		pTextCtrl->SetDefaultStyle(originalAttr);
 
 		pTextCtrl->AppendText
 		);
 
 		pTextCtrl->SetDefaultStyle(underlinedAttr);
-		pTextCtrl->AppendText(L"Additional informations:\n\n");
+		pTextCtrl->AppendText(_("Additional informations:\n\n"));
 		pTextCtrl->SetDefaultStyle(originalAttr);
 
 		pTextCtrl->AppendText
 			L"This program is powered by Open Steamworks ( http://opensteamworks.org/ ).\n"
 		);
 
-		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxBOTH);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
 		m_pHelpPanel->SetSizer(pBoxSizer);
 		pBoxSizer->Add(pTextCtrl, 1, wxEXPAND);
 	}
 	m_pLoggingPanel->SetSizer(pBoxSizer);
 
 	wxGridBagSizer *pGridBagSizer = new wxGridBagSizer(10, 10);
-	pGridBagSizer->AddGrowableCol(1);
+	pGridBagSizer->AddGrowableCol(0);
 	pBoxSizer->Add(pGridBagSizer, 1, wxALL | wxEXPAND, 10);
 
 	int iRow = 0;
 		tags.Add(_T("{MySteamID}"));	replacements.Add(wxString(mySteamID.SteamRender(), wxConvUTF8));
 		tags.Add(_T("{SteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), friendSteamID.ConvertToUint64()));
 		tags.Add(_T("{MySteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), mySteamID.ConvertToUint64()));
-		tags.Add(_T("{Name}"));			replacements.Add(_T("Christopher"));
-		tags.Add(_T("{Nickname}"));		replacements.Add(_T("Chris"));
-		tags.Add(_T("{MyName}"));		replacements.Add(_T("Your Name"));
+		tags.Add(_T("{Name}"));			replacements.Add(_("Christopher"));
+		tags.Add(_T("{Nickname}"));		replacements.Add(_("Chris"));
+		tags.Add(_T("{MyName}"));		replacements.Add(_("Your Name"));
 		tags.Add(_T("{Date}"));			replacements.Add(wxDateTime::Now().Format(m_pDateInput->GetValue()));
 		tags.Add(_T("{Time}"));			replacements.Add(wxDateTime::Now().Format(m_pTimeInput->GetValue()));
 		tags.Add(_T("{UnixTime}"));		replacements.Add(wxString::Format(_T("%lld"), (long long)wxDateTime::GetTimeNow()));
 		tags.Add(_T("{MySteamID}"));	replacements.Add(wxString(mySteamID.SteamRender(), wxConvUTF8));
 		tags.Add(_T("{SteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), friendSteamID.ConvertToUint64()));
 		tags.Add(_T("{MySteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), mySteamID.ConvertToUint64()));
-		tags.Add(_T("{Name}"));			replacements.Add(_T("Christopher"));
-		tags.Add(_T("{Nickname}"));		replacements.Add(_T("Chris"));
-		tags.Add(_T("{MyName}"));		replacements.Add(_T("Your Name"));
+		tags.Add(_T("{Name}"));			replacements.Add(_("Christopher"));
+		tags.Add(_T("{Nickname}"));		replacements.Add(_("Chris"));
+		tags.Add(_T("{MyName}"));		replacements.Add(_("Your Name"));
 		tags.Add(_T("{Date}"));			replacements.Add(wxDateTime::Now().Format(m_pDateInput->GetValue()));
 		tags.Add(_T("{Time}"));			replacements.Add(wxDateTime::Now().Format(m_pTimeInput->GetValue()));
 		tags.Add(_T("{UnixTime}"));		replacements.Add(wxString::Format(_T("%lld"), (long long)wxDateTime::GetTimeNow()));
 		tags.Add(_T("{NewLine}"));		replacements.Add(_T("\n"));
 		tags.Add(_T("{Tab}"));			replacements.Add(_T("\t"));
-		tags.Add(_T("{Message}"));		replacements.Add(_T("Hello there!"));
+		tags.Add(_T("{Message}"));		replacements.Add(_("Hello there!"));
 
 		preview = CLogger::TagsReplace(preview, tags, replacements);
 
 		tags.Add(_T("{MySteamID}"));	replacements.Add(wxString(mySteamID.SteamRender(), wxConvUTF8));
 		tags.Add(_T("{SteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), friendSteamID.ConvertToUint64()));
 		tags.Add(_T("{MySteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), mySteamID.ConvertToUint64()));
-		tags.Add(_T("{Name}"));			replacements.Add(_T("Christopher"));
-		tags.Add(_T("{Nickname}"));		replacements.Add(_T("Chris"));
-		tags.Add(_T("{MyName}"));		replacements.Add(_T("Your Name"));
+		tags.Add(_T("{Name}"));			replacements.Add(_("Christopher"));
+		tags.Add(_T("{Nickname}"));		replacements.Add(_("Chris"));
+		tags.Add(_T("{MyName}"));		replacements.Add(_("Your Name"));
 		tags.Add(_T("{Date}"));			replacements.Add(wxDateTime::Now().Format(m_pDateInput->GetValue()));
 		tags.Add(_T("{Time}"));			replacements.Add(wxDateTime::Now().Format(m_pTimeInput->GetValue()));
 		tags.Add(_T("{UnixTime}"));		replacements.Add(wxString::Format(_T("%lld"), (long long)wxDateTime::GetTimeNow()));
 		tags.Add(_T("{NewLine}"));		replacements.Add(_T("\n"));
 		tags.Add(_T("{Tab}"));			replacements.Add(_T("\t"));
-		tags.Add(_T("{Message}"));		replacements.Add(_T("does an emote."));
+		tags.Add(_T("{Message}"));		replacements.Add(_("does an emote."));
 
 		preview = CLogger::TagsReplace(preview, tags, replacements);
 

Projects/Chat Logger++/ConfigurationDialog.h

 #pragma once
 
 #include <wx/wx.h>
-#include <wx/msw/regconf.h>
+#include <wx/config.h>
 #include <wx/notebook.h>
 #include <wx/filepicker.h>
 

Projects/Chat Logger++/Logger.cpp

 
 #include <wx/wx.h>
 #include <wx/msw/regconf.h>
+#include "SigScanner.h"
 #include "Logger.h"
 #include "App.h"
 
 	m_pClientFriends = NULL;
 	m_pSteamUser = NULL;
 
+	m_pGetChatRoomEntry = NULL;
+	m_pGetChatRoomName = NULL;
+	m_pGetPlayerNickname = NULL;
+
 	wxRegConfig config(_T("Chat Logger++"), _T("OSW"));
 	config.Read(_T("LogDirectory"), &m_logDirectory, wxGetCwd());
 	config.Read(_T("FilenameFormat"), &m_filenameFormat, _T("{SteamID} - {Nickname}.txt"));
 	m_timeFormat = format;
 }
 
+void* CLogger::FindSteamFunction(const char* cszName)
+{
+	static CSigScanner sigScanner(GetModuleHandleA("steamclient.dll"));
+
+	for(unsigned char* pMatch = NULL; pMatch = (unsigned char*)sigScanner.FindSignature("\x68\x00\x00\x00\x00\x51\x8d\x55\xd0\x52\x50\xe8\x00\x00\x00\x00\x8d\x48\x04\xe8\x00\x00\x00\x00\x8b\xf0\x8b\xce\xe8\x00\x00\x00\x00\x3c\x01\x74\x00", "x????xxx?xxx????xxxx????xxxxx????xxx?", false, pMatch); )
+	{
+		const char* cszFunctionName = *(const char**)(pMatch + 1);
+		if(strcmp(cszFunctionName, cszName) == 0)
+		{
+			return sigScanner.FindSignature("\x55\x8B\xEC\x83\xEC", "xxxxx", true, pMatch);
+		}
+	}
+
+	return NULL;
+}
+
 bool CLogger::InitSteam()
 {
 	CreateInterfaceFn steam3Factory = m_steamLoader.GetSteam3Factory();
 	if(!m_pSteamUser)
 		return false;
 
+	wxGetApp().SetTrayIcon(CApp::k_EIconNormal);
+	wxGetApp().Message(_("Connected to Steam."));
+
 	if(m_bUseClientInterfaces)
 	{
 		IClientEngine* pClientEngine = (IClientEngine*)steam3Factory(CLIENTENGINE_INTERFACE_VERSION, NULL);
 		if(!pClientEngine)
-			return false;
+		{
+			wxGetApp().SetTrayIcon(CApp::k_EIconWarning, _("Group chat logging and Nickname support are disabled"));
+			wxGetApp().Warning(_("Unable to get IClientEngine interface,\nGroup chat logging and Nickname support are disabled."));
+			return true;
+		}
 
 		m_pClientFriends = pClientEngine->GetIClientFriends(m_hUser, m_hPipe, CLIENTFRIENDS_INTERFACE_VERSION);
 		if(!m_pClientFriends)
-			return false;
+		{
+			wxGetApp().SetTrayIcon(CApp::k_EIconWarning, _("Group chat logging and Nickname support are disabled"));
+			wxGetApp().Warning(_("Unable to get IClientFriends interface,\nGroup chat logging and Nickname support are disabled."));
+			return true;
+		}
+
+		m_pGetChatRoomEntry = (int32 (__thiscall *)(IClientFriends *,CSteamID, int32, CSteamID *, void *, int32, EChatEntryType *)) FindSteamFunction("GetChatRoomEntry");
+		if(!m_pGetChatRoomEntry)
+		{
+			wxGetApp().SetTrayIcon(CApp::k_EIconWarning, _("Group chat logging is disabled"));
+			wxGetApp().Warning(_("Unable to find GetChatRoomEntry,\nGroup chat logging is disabled."));
+		}
+		
+		m_pGetChatRoomName = (const char *(__thiscall *)(IClientFriends *, CSteamID)) FindSteamFunction("GetChatRoomName");
+		if(!m_pGetChatRoomName)
+		{
+			wxGetApp().SetTrayIcon(CApp::k_EIconWarning, _("{Name} variable is disabled"));
+			wxGetApp().Warning(_("Unable to find GetChatRoomName,\nThe {Name} variable won't work for chat rooms."));
+		}
+		
+		m_pGetPlayerNickname = (const char *(__thiscall *)(IClientFriends *, CSteamID)) FindSteamFunction("GetPlayerNickname");
+		if(!m_pGetPlayerNickname)
+		{
+			wxGetApp().SetTrayIcon(CApp::k_EIconWarning, _("Nickname support is disabled"));
+			wxGetApp().Warning(_("Unable to find GetPlayerNickname,\nNickname support is disabled."));
+		}
 	}
 
-	wxGetApp().Message(_("Connected to Steam."));
-
 	return true;
 }
 
 	m_pSteamFriends = NULL;
 	m_pClientFriends = NULL;
 	m_pSteamUser = NULL;
+
+	m_pGetChatRoomEntry = NULL;
+	m_pGetChatRoomName = NULL;
+	m_pGetPlayerNickname = NULL;
 }
 
 void CLogger::UseClientInterfaces(bool bUse)
 	m_pSteamFriends = NULL;
 	m_pSteamUser = NULL;
 
+	m_pGetChatRoomEntry = NULL;
+	m_pGetChatRoomName = NULL;
+	m_pGetPlayerNickname = NULL;
+
 	CloseLogs();
 
+	wxGetApp().SetTrayIcon(CApp::k_EIconDisconnected, _("Connection to Steam lost"));
 	wxGetApp().Warning(_("Connection to Steam lost."));
 }
 
 
 void CLogger::OnChatRoomMsg(ChatRoomMsg_t* pChatRoomMsg)
 {
-	if(!m_pClientFriends)
+	if(!m_pClientFriends || !m_pGetChatRoomEntry)
 		return;
 
 	char szMessage[k_cchFriendChatMsgMax + 1];
 	EChatEntryType eEntryType;
 	CSteamID chatterID;
-	int iMessageSize = m_pClientFriends->GetChatRoomEntry(pChatRoomMsg->m_ulSteamIDChat, pChatRoomMsg->m_iChatID, &chatterID, szMessage, sizeof(szMessage) - 1, &eEntryType);
+	int iMessageSize = m_pGetChatRoomEntry(m_pClientFriends, pChatRoomMsg->m_ulSteamIDChat, pChatRoomMsg->m_iChatID, &chatterID, szMessage, sizeof(szMessage) - 1, &eEntryType);
 	szMessage[iMessageSize] = '\0';
 
 	if(iMessageSize)
 		{
 			for(size_t j = 0; j < tags.Count(); j++)
 			{
-				wxString& tag = tags[j];
+				const wxString& tag = tags[j];
 				if(string.substr(i, tag.length()) == tag)
 				{
 					ret += replacements[j];
 	}
 	else
 	{
-		const char* cszChatRoomName = m_pClientFriends->GetChatRoomName(steamID);
+		const char* cszChatRoomName = NULL;
+		
+		if(m_pClientFriends && m_pGetChatRoomName)
+			cszChatRoomName = m_pGetChatRoomName(m_pClientFriends, steamID);
+
 		if(*cszChatRoomName)
 		{
 			replacements.Add(wxString(cszChatRoomName, wxConvUTF8));
 	{
 		const char* cszNickname = NULL;
 
-		if(m_pClientFriends)
-			cszNickname = m_pClientFriends->GetPlayerNickname(steamID);
+		if(m_pClientFriends && m_pGetPlayerNickname)
+			cszNickname = m_pGetPlayerNickname(m_pClientFriends, steamID);
 
 		if(cszNickname)
 		{
 	wxString name = wxString(m_pSteamFriends->GetFriendPersonaName(steamID), wxConvUTF8);
 	const char* cszNickname = NULL;
 
-	if(m_pClientFriends)
-		cszNickname = m_pClientFriends->GetPlayerNickname(steamID);
+	if(m_pClientFriends && m_pGetPlayerNickname)
+		cszNickname = m_pGetPlayerNickname(m_pClientFriends, steamID);
 
 	if(cszNickname)
 	{

Projects/Chat Logger++/Logger.h

 	void CleanupSteam();
 	void CallbackHandlerThread();
 
+	void* FindSteamFunction(const char* cszName);
+
 	void OnIPCFailure(IPCFailure_t* pIPCFailure);
 	void OnFriendChatMsg(FriendChatMsg_t* pFriendChatMsg);
 	void OnChatRoomMsg(ChatRoomMsg_t* pChatRoomMsg);
 	ISteamUser016* m_pSteamUser;
 	ISteamFriends013* m_pSteamFriends;
 	IClientFriends* m_pClientFriends;
+	
+	int32 (__thiscall *m_pGetChatRoomEntry)( IClientFriends*, CSteamID steamIDChat, int32 iChatID, CSteamID *steamIDuser, void *pvData, int32 cubData, EChatEntryType *peChatEntryType );
+	const char * (__thiscall *m_pGetChatRoomName)( IClientFriends*, CSteamID steamIDChat );
+	const char * (__thiscall *m_pGetPlayerNickname)( IClientFriends*, CSteamID playerSteamID );
 
 	std::map<CSteamID, wxFFile*> m_logsOpened;
 

Projects/Chat Logger++/SigScanner.cpp

+/*
+	This file is a part of "Chat Logger++"
+	�2k12, Didrole
+	
+	License : Public domain
+*/
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "SigScanner.h"
+
+
+CSigScanner::CSigScanner(const void* hModule) : m_pAllocationBase(NULL), m_uSize(0)
+{
+	if(!hModule)
+		return;
+
+	MEMORY_BASIC_INFORMATION basicInformation;
+
+	if(!VirtualQuery(hModule, &basicInformation, sizeof(basicInformation)))
+		return;
+
+	const IMAGE_DOS_HEADER *pDOSHeader = (IMAGE_DOS_HEADER*)basicInformation.AllocationBase;
+	const IMAGE_NT_HEADERS *pNTHeader = (IMAGE_NT_HEADERS*)(((unsigned char*)basicInformation.AllocationBase) + pDOSHeader->e_lfanew);
+
+	if(pNTHeader->Signature != IMAGE_NT_SIGNATURE)
+		return;
+
+	m_pAllocationBase = basicInformation.AllocationBase;
+	m_uSize = pNTHeader->OptionalHeader.SizeOfImage;
+}
+
+void* CSigScanner::FindSignature(const unsigned char* pubSignature, const char* cszMask, bool bSearchUp/* = false*/, const void* pPreviousMatch/* = NULL*/) const
+{
+	if(!m_pAllocationBase || !cszMask || !*cszMask)
+		return NULL;
+
+	unsigned char *pCurrent = NULL;
+
+	unsigned int uSignatureLength = (unsigned int)strlen(cszMask);
+
+	if(!bSearchUp)
+	{
+		if(pPreviousMatch)
+		{
+			if(pPreviousMatch < m_pAllocationBase || (unsigned char*)pPreviousMatch + uSignatureLength > (unsigned char*)m_pAllocationBase + m_uSize)
+				return NULL;
+
+			pCurrent = (unsigned char*)pPreviousMatch + 1;
+		}
+		else
+			pCurrent = (unsigned char *)m_pAllocationBase;
+
+		unsigned char *pEnd = (unsigned char*)m_pAllocationBase + m_uSize;
+
+		unsigned int i;
+
+		for(; pCurrent < pEnd && (unsigned long)(pEnd - pCurrent) >= uSignatureLength; pCurrent++)
+		{
+			for(i = 0; cszMask[i] != '\0'; i++)
+			{
+				if((cszMask[i] != '?') && (pubSignature[i] != pCurrent[i]))
+					break;
+			}
+			if(cszMask[i] == '\0')
+				return pCurrent;
+		}
+		return NULL;
+	}
+	else
+	{
+		if(pPreviousMatch)
+		{
+			if((unsigned char*)pPreviousMatch - uSignatureLength < m_pAllocationBase || pPreviousMatch > (unsigned char*)m_pAllocationBase + m_uSize)
+				return NULL;
+
+			pCurrent = (unsigned char*)pPreviousMatch - 1;
+		}
+		else
+			pCurrent = (unsigned char*)m_pAllocationBase + m_uSize - uSignatureLength;
+
+		unsigned char *pEnd = (unsigned char*)m_pAllocationBase;
+
+		unsigned int i;
+
+		for(; pCurrent > pEnd && (unsigned long)(pCurrent - pEnd) >= uSignatureLength; pCurrent--)
+		{
+			for(i = 0; cszMask[i] != '\0'; i++)
+			{
+				if((cszMask[i] != '?') && (pubSignature[i] != pCurrent[i]))
+					break;
+			}
+			if(cszMask[i] == '\0')
+				return pCurrent;
+		}
+		return NULL;
+	}
+}

Projects/Chat Logger++/SigScanner.h

+/*
+	This file is a part of "Chat Logger++"
+	�2k12, Didrole
+	
+	License : Public domain
+*/
+
+#pragma once
+
+class CSigScanner
+{
+public:
+	CSigScanner(const void* hModule);
+
+	void* FindSignature(const char* pubSignature, const char* cszMask, bool bSearchUp = false, const void* pPreviousMatch = NULL) const;
+	void* FindSignature(const unsigned char* pubSignature, const char* cszMask, bool bSearchUp = false, const void* pPreviousMatch = NULL) const;
+
+private:
+	const void* m_pAllocationBase;
+	unsigned int m_uSize;
+};
+
+inline void* CSigScanner::FindSignature(const char* pubSignature, const char* cszMask, bool bSearchUp/* = false*/, const void* pPreviousMatch/* = NULL*/) const
+{
+	return FindSignature((const unsigned char*)pubSignature, cszMask, bSearchUp, pPreviousMatch);
+}

Projects/Chat Logger++/app.ico

Binary file modified.

Projects/Chat Logger++/app_disconnected.ico

Binary file added.

Projects/Chat Logger++/app_warning.ico

Binary file added.

Projects/Chat Logger++/resource.rc

 #include "afxres.h"
 
-AppIcon               ICON                    "app.ico"
-
+1_AppIcon               ICON                    "app.ico"
+2_AppWarningIcon        ICON                    "app_warning.ico"
+3_AppDisconnectedIcon   ICON                    "app_disconnected.ico"
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,0,0
- PRODUCTVERSION 1,0,0,0
+ FILEVERSION 1,2,0,0
+ PRODUCTVERSION 1,2,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
  FILEFLAGS 0x1L
     BEGIN
         BLOCK "000904b0"
         BEGIN
-            VALUE "FileVersion", "1, 0, 0, 0"
+            VALUE "FileVersion", "1, 2, 0, 0"
             VALUE "LegalCopyright", "�2k12, Didrole"
             VALUE "ProductName", "Chat Logger++"
-            VALUE "ProductVersion", "1, 0, 0, 0"
+            VALUE "ProductVersion", "1, 2, 0, 0"
         END
     END
     BLOCK "VarFileInfo"