Commits

Didrole committed 1742668

Added Chat Logger++

Comments (0)

Files changed (12)

Projects/Chat Logger++/App.cpp

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#include <wx/stdpaths.h>
+#include "App.h"
+
+#include "TaskBarIcon.h"
+#include "ConfigurationDialog.h"
+#include "Logger.h"
+
+enum
+{
+	wxID_AUTOSTART = 1,
+};
+
+BEGIN_EVENT_TABLE(CApp, wxApp)
+	EVT_TASKBAR_LEFT_DCLICK(OnTaskBarIconClick)
+	EVT_TASKBAR_RIGHT_DOWN(OnTaskBarIconClick)
+	EVT_MENU(wxID_AUTOSTART, OnAutoStartButtonClick)
+	EVT_MENU(wxID_OPEN, OnConfigurationButtonClick)
+	EVT_MENU(wxID_EXIT, OnExitButtonClick)
+END_EVENT_TABLE()
+
+IMPLEMENT_APP(CApp)
+
+
+CApp::CApp()
+{
+	m_pConfigurationDialog = NULL;
+	m_pTaskBarIcon = NULL;
+	m_pLogger = NULL;
+}
+
+void CApp::InvalidParameterHandler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved)
+{
+}
+
+bool CApp::OnInit()
+{
+	_set_invalid_parameter_handler(&InvalidParameterHandler);
+
+	m_pTaskBarIcon = new CTaskBarIcon();
+	m_pTaskBarIcon->SetIcon(wxICON(AppIcon), L"Chat Logger++");
+
+	m_pLogger = new CLogger();
+
+	return true;
+}
+
+void CApp::CleanUp()
+{
+	if(m_pLogger)
+	{
+		m_pLogger->Delete();
+		//delete m_pLogger;
+	}
+
+	if(m_pTaskBarIcon)
+	{
+		m_pTaskBarIcon->RemoveIcon();
+		delete m_pTaskBarIcon;
+	}
+
+	if(m_pConfigurationDialog)
+		m_pConfigurationDialog->Destroy();
+}
+
+void CApp::OnTaskBarIconClick(wxTaskBarIconEvent& event)
+{
+	bool bAutoRun = false;
+
+	HKEY hkRegistry;
+	if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\", 0, KEY_QUERY_VALUE, &hkRegistry) == ERROR_SUCCESS)
+	{
+		wchar_t wszPath[4096];
+		memset(wszPath, 0, sizeof(wszPath));
+		DWORD dwType, dwSize = sizeof(wszPath) - 1;
+		if(RegQueryValueExW(hkRegistry, L"Chat Logger++", 0, &dwType, (unsigned char *)wszPath, &dwSize) == ERROR_SUCCESS)
+		{
+			if(wxString(wxStandardPaths::Get().GetExecutablePath()).CmpNoCase(wszPath) == 0)
+				bAutoRun = true;
+		}
+		RegCloseKey(hkRegistry);
+	}
+
+	wxMenu menu;
+	menu.Append(wxID_OPEN, L"Configuration");
+	menu.AppendSeparator();
+	menu.Append(wxID_AUTOSTART, L"Start with Windows", wxEmptyString, wxITEM_CHECK)->Check(bAutoRun);
+	menu.AppendSeparator();
+	menu.Append(wxID_EXIT);
+
+	m_pTaskBarIcon->PopupMenu(&menu);
+
+	event.Skip();
+}
+
+void CApp::OnExitButtonClick(wxCommandEvent& event)
+{
+	this->ExitMainLoop();
+}
+
+void CApp::OnAutoStartButtonClick(wxCommandEvent& event)
+{
+	HKEY hkRegistry;
+	if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run\\", 0, KEY_WRITE, &hkRegistry) == ERROR_SUCCESS)
+	{
+		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));
+		}
+		else
+		{
+			RegDeleteValueA(hkRegistry, "Chat Logger++");
+		}
+		RegCloseKey(hkRegistry);
+	}
+}
+
+void CApp::OnConfigurationWindowClosed(wxCloseEvent& event)
+{
+	m_pConfigurationDialog->Destroy();
+	m_pConfigurationDialog = NULL;
+}
+
+void CApp::OnConfigurationButtonClick(wxCommandEvent& event)
+{
+	if(!m_pConfigurationDialog)
+	{
+		m_pConfigurationDialog = new CConfigurationDialog();
+		m_pConfigurationDialog->Connect(wxEVT_CLOSE_WINDOW, (wxObjectEventFunction)&CApp::OnConfigurationWindowClosed, NULL, this);
+	}
+
+	m_pConfigurationDialog->Show();
+
+	if(m_pConfigurationDialog)
+		m_pConfigurationDialog->Raise();
+}
+
+void CApp::Message(const wxString& message)
+{
+	m_pTaskBarIcon->ShowMessage(_T(""), message, CTaskBarIcon::k_EIconInfo);
+}
+
+void CApp::Warning(const wxString& message)
+{
+	m_pTaskBarIcon->ShowMessage(_T(""), message, CTaskBarIcon::k_EIconWarning);
+}
+
+CLogger* CApp::GetLogger()
+{
+	return m_pLogger;
+}

Projects/Chat Logger++/App.h

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#pragma once
+
+#include <wx/wx.h>
+#include <wx/taskbar.h>
+
+class CConfigurationDialog;
+class CTaskBarIcon;
+class CLogger;
+
+class CApp : public wxApp
+{
+public:
+	CApp();
+    virtual bool OnInit();
+	virtual void CleanUp();
+
+	void Message(const wxString& message);
+	void Warning(const wxString& message);
+
+	CLogger* GetLogger();
+
+private:
+	static void InvalidParameterHandler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved);
+
+	void OnTaskBarIconClick(wxTaskBarIconEvent& event);
+	void OnAutoStartButtonClick(wxCommandEvent& event);
+	void OnConfigurationButtonClick(wxCommandEvent& event);
+	void OnExitButtonClick(wxCommandEvent& event);
+	void OnConfigurationWindowClosed(wxCloseEvent& event);
+
+	CConfigurationDialog* m_pConfigurationDialog;
+	CTaskBarIcon* m_pTaskBarIcon;
+
+	CLogger* m_pLogger;
+
+	DECLARE_EVENT_TABLE()
+};
+DECLARE_APP(CApp)

Projects/Chat Logger++/Chat Logger++.sln

+ďťż
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chat Logger++", "Chat Logger++.vcproj", "{A73967E7-82EB-4B77-B765-02AE2A6B1B08}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{A73967E7-82EB-4B77-B765-02AE2A6B1B08}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A73967E7-82EB-4B77-B765-02AE2A6B1B08}.Debug|Win32.Build.0 = Debug|Win32
+		{A73967E7-82EB-4B77-B765-02AE2A6B1B08}.Release|Win32.ActiveCfg = Release|Win32
+		{A73967E7-82EB-4B77-B765-02AE2A6B1B08}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

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

+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9,00"
+	Name="Chat Logger++"
+	ProjectGUID="{A73967E7-82EB-4B77-B765-02AE2A6B1B08}"
+	RootNamespace="Chat Logger++"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(WXWIN28)\include\msvc&quot;;&quot;$(WXWIN28)\include&quot;;&quot;..\..\Open Steamworks&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;STEAMWORKS_CLIENT_INTERFACES"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="comctl32.lib rpcrt4.lib gdiplus.lib"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories="$(WXWIN28)\lib\vc_lib"
+				IgnoreDefaultLibraryNames=""
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="3"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="1"
+				OmitFramePointers="true"
+				AdditionalIncludeDirectories="&quot;$(WXWIN28)\include\msvc&quot;;&quot;$(WXWIN28)\include&quot;;&quot;..\..\Open Steamworks&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;STEAMWORKS_CLIENT_INTERFACES"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="false"
+				EnableEnhancedInstructionSet="0"
+				FloatingPointModel="2"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="comctl32.lib rpcrt4.lib gdiplus.lib"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="$(WXWIN28)\lib\vc_lib"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\App.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ConfigurationDialog.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Logger.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\TaskBarIcon.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\App.h"
+				>
+			</File>
+			<File
+				RelativePath=".\ConfigurationDialog.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Logger.h"
+				>
+			</File>
+			<File
+				RelativePath=".\TaskBarIcon.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+			<File
+				RelativePath=".\app.ico"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\resource.rc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\Resources\Libs\Win32\steamclient.lib"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

Projects/Chat Logger++/ConfigurationDialog.cpp

+ďťż/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#include <wx/statline.h>
+#include "ConfigurationDialog.h"
+#include "Logger.h"
+#include "App.h"
+#include <wx/gbsizer.h>
+
+enum
+{
+	wxID_LogDirPicker = 0,
+	wxID_FilenameInput,
+	wxID_ReplacementInput,
+	wxID_MessageInput,
+	wxID_EmoteInput,
+	wxID_SeparationInput,
+	wxID_DateInput,
+	wxID_TimeInput,
+};
+
+BEGIN_EVENT_TABLE(CConfigurationDialog, wxDialog)
+	EVT_TEXT(wxID_FilenameInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT(wxID_ReplacementInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT(wxID_MessageInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT(wxID_EmoteInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT(wxID_DateInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT(wxID_TimeInput, CConfigurationDialog::OnInputChange)
+	EVT_TEXT_URL(wxID_ANY, CConfigurationDialog::OnTextURLEvent)
+	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"))
+{
+	m_bEnablePreviews = false;
+
+	m_pNotebook = new wxNotebook(this, wxID_ANY);
+
+	m_pLoggingPanel = new wxPanel(m_pNotebook, wxID_ANY);
+	m_pNotebook->AddPage(m_pLoggingPanel, _("Logging"));
+
+	m_pHelpPanel = new wxPanel(m_pNotebook, wxID_ANY);
+	m_pNotebook->AddPage(m_pHelpPanel, _("Help && Formatting"));
+	{
+		wxTextCtrl* pTextCtrl = new wxTextCtrl(m_pHelpPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_RICH | wxTE_AUTO_URL | wxTE_READONLY);
+		
+		wxTextAttr originalAttr = pTextCtrl->GetDefaultStyle();
+		wxTextAttr underlinedAttr = originalAttr;
+		wxFont underlinedFont = pTextCtrl->GetDefaultStyle().GetFont();
+		underlinedFont.SetUnderlined(true);
+		underlinedAttr.SetFont(underlinedFont);
+
+		pTextCtrl->SetDefaultStyle(underlinedAttr);
+		pTextCtrl->AppendText(L"Common variables:\n\n");
+		pTextCtrl->SetDefaultStyle(originalAttr);
+
+		pTextCtrl->AppendText
+		(
+			L"{Name}		- Sender's friend's name.\n"
+			L"{Nickname}	- Sender's nickname, if it exists, otherwise their name.\n"
+			L"{SteamID}	- Sender's SteamID.\n"
+			L"{SteamID64}	- Sender's SteamID rendered as a 64bits integer.\n"
+			L"\n"
+			L"{MyName}	- Your current friend's name.\n"
+			L"{MySteamID}	- Your SteamID.\n"
+			L"{MySteamID64}	- Your SteamID rendered as a 64bits integer.\n"
+			L"\n"
+			L"{Date}		- Current date.\n"
+			L"{Time}		- Current time.\n"
+			L"{UnixTime}	- Seconds since unix epoch.\n"
+			L"\n"
+		);
+
+		pTextCtrl->SetDefaultStyle(underlinedAttr);
+		pTextCtrl->AppendText(L"Message specific variables:\n\n");
+		pTextCtrl->SetDefaultStyle(originalAttr);
+
+		pTextCtrl->AppendText
+		(
+			L"{NewLine}	- New line character \"\\n\".\n"
+			L"{Tab}		- Tab character \"\\t\".\n"
+			L"\n"
+			L"{Message}	- The actual chat message.\n"
+			L"\n"
+		);
+
+		pTextCtrl->SetDefaultStyle(underlinedAttr);
+		pTextCtrl->AppendText(L"Additional informations:\n\n");
+		pTextCtrl->SetDefaultStyle(originalAttr);
+
+		pTextCtrl->AppendText
+		(
+			L"The format used for date and time is described here : http://www.cplusplus.com/reference/clibrary/ctime/strftime/ .\n"
+			L"\n"
+			L"This program is powered by Open Steamworks ( http://opensteamworks.org/ ).\n"
+		);
+
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxBOTH);
+		m_pHelpPanel->SetSizer(pBoxSizer);
+		pBoxSizer->Add(pTextCtrl, 1, wxEXPAND);
+	}
+
+	wxBoxSizer *pBoxSizer = new wxBoxSizer(wxHORIZONTAL);
+	m_pLoggingPanel->SetSizer(pBoxSizer);
+
+	wxGridBagSizer *pGridBagSizer = new wxGridBagSizer(10, 10);
+	pGridBagSizer->AddGrowableCol(1);
+	pBoxSizer->Add(pGridBagSizer, 1, wxALL | wxEXPAND, 10);
+
+	int iRow = 0;
+
+	wxString logDirectory;
+	m_config.Read(_T("LogDirectory"), &logDirectory, wxGetCwd());
+
+	wxStaticText *pLogDirLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Log directory: "));
+	m_pLogDirPicker = new wxDirPickerCtrl(m_pLoggingPanel, wxID_LogDirPicker, logDirectory);
+	pGridBagSizer->Add(pLogDirLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+	pGridBagSizer->Add(m_pLogDirPicker, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+
+	{
+		wxString filenameFormat;
+		m_config.Read(_T("FilenameFormat"), &filenameFormat, _T("{SteamID} - {Nickname}.txt"));
+
+		wxStaticText *pFilenameLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Filename format: "));
+		pGridBagSizer->Add(pFilenameLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		m_pFilenameInput = new wxTextCtrl(pPanel, wxID_FilenameInput, filenameFormat);
+		pBoxSizer->Add(m_pFilenameInput, 0, wxEXPAND);
+		m_pFilenamePreviewLabel = new wxStaticText(pPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
+		pBoxSizer->Add(m_pFilenamePreviewLabel, 1, wxEXPAND | wxALL, 5);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+	wxString replacementChar;
+	m_config.Read(_T("ReplacementChar"), &replacementChar, _T("_"));
+
+	wxStaticText *pReplacementLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Invalid char replacement: "));
+	m_pReplacementInput = new wxTextCtrl(m_pLoggingPanel, wxID_ReplacementInput, replacementChar);
+	m_pReplacementInput->SetMaxLength(1);
+	pGridBagSizer->Add(pReplacementLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+	pGridBagSizer->Add(m_pReplacementInput, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+
+	{
+		wxStaticLine* pLine = new wxStaticLine(m_pLoggingPanel);
+		pGridBagSizer->Add(pLine, wxGBPosition(iRow++, 0), wxGBSpan(1, 2), wxEXPAND);
+	}
+
+	{
+		wxString messageFormat;
+		m_config.Read(_T("MessageFormat"), &messageFormat, _T("[{Time}] {Name}: {Message}"));
+
+		wxStaticText *pMessageLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Message format: "));
+		pGridBagSizer->Add(pMessageLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		m_pMessageInput = new wxTextCtrl(pPanel, wxID_MessageInput, messageFormat);
+		pBoxSizer->Add(m_pMessageInput, 0, wxEXPAND);
+		m_pMessagePreviewLabel = new wxStaticText(pPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
+		pBoxSizer->Add(m_pMessagePreviewLabel, 1, wxEXPAND | wxALL, 5);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+	{
+		wxString emoteFormat;
+		m_config.Read(_T("EmoteFormat"), &emoteFormat, _T("[{Time}] * {Name} {Message}"));
+
+		wxStaticText *pEmoteLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Emote format: "));
+		pGridBagSizer->Add(pEmoteLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		m_pEmoteInput = new wxTextCtrl(pPanel, wxID_EmoteInput, emoteFormat);
+		pBoxSizer->Add(m_pEmoteInput, 0, wxEXPAND);
+		m_pEmotePreviewLabel = new wxStaticText(pPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
+		pBoxSizer->Add(m_pEmotePreviewLabel, 1, wxEXPAND | wxALL, 5);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+	{
+		wxString separationString;
+		m_config.Read(_T("SeparationString"), &separationString, _T("───────────────────"));
+
+		wxStaticText *pSeparationLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Separation string: "));
+		pGridBagSizer->Add(pSeparationLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		m_pSeparationInput = new wxTextCtrl(m_pLoggingPanel, wxID_SeparationInput, separationString);
+		pGridBagSizer->Add(m_pSeparationInput, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+	{
+		wxStaticLine* pLine = new wxStaticLine(m_pLoggingPanel);
+		pGridBagSizer->Add(pLine, wxGBPosition(iRow++, 0), wxGBSpan(1, 2), wxEXPAND);
+	}
+
+	{
+		wxString dateFormat;
+		m_config.Read(_T("DateFormat"), &dateFormat, _T("%x"));
+
+		wxStaticText *pDateLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Date format: "));
+		pGridBagSizer->Add(pDateLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		m_pDateInput = new wxTextCtrl(pPanel, wxID_DateInput, dateFormat);
+		pBoxSizer->Add(m_pDateInput, 0, wxEXPAND);
+		m_pDatePreviewLabel = new wxStaticText(pPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
+		pBoxSizer->Add(m_pDatePreviewLabel, 1, wxEXPAND | wxALL, 5);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+	{
+		wxString timeFormat;
+		m_config.Read(_T("TimeFormat"), &timeFormat, _T("%X"));
+
+		wxStaticText *pTimeLabel = new wxStaticText(m_pLoggingPanel, wxID_ANY, _("Time format: "));
+		pGridBagSizer->Add(pTimeLabel, wxGBPosition(iRow, 0), wxDefaultSpan, wxTOP, 3);
+
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		m_pTimeInput = new wxTextCtrl(pPanel, wxID_TimeInput, timeFormat);
+		pBoxSizer->Add(m_pTimeInput, 0, wxEXPAND);
+		m_pTimePreviewLabel = new wxStaticText(pPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
+		pBoxSizer->Add(m_pTimePreviewLabel, 1, wxEXPAND | wxALL, 5);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 1), wxDefaultSpan, wxEXPAND);
+	}
+
+
+	{
+		wxStaticLine* pLine = new wxStaticLine(m_pLoggingPanel);
+		pGridBagSizer->Add(pLine, wxGBPosition(iRow++, 0), wxGBSpan(1, 2), wxEXPAND);
+	}
+
+	{
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxVERTICAL);
+
+		bool bEnabled;
+		m_config.Read(_T("Unstable"), &bEnabled, false);
+
+		pUnstableCheckBox = new wxCheckBox(pPanel, wxID_ANY, _("Enable Group chat logging and Nickname support."));
+		pUnstableCheckBox->SetValue(bEnabled);
+		wxStaticText *pLabel = new wxStaticText(pPanel, wxID_ANY, _("These features are unstable and can break with any Steam update."));
+		
+		pBoxSizer->Add(pUnstableCheckBox);
+		pBoxSizer->Add(pLabel, 0, wxLEFT, 17);
+		pPanel->SetSizer(pBoxSizer);
+		
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 0), wxGBSpan(1, 2));
+	}
+
+	{
+		wxPanel* pPanel = new wxPanel(m_pLoggingPanel);
+		wxBoxSizer *pBoxSizer = new wxBoxSizer(wxHORIZONTAL);
+		wxButton* pOKButton = new wxButton(pPanel, wxID_OK);
+		pBoxSizer->Add(pOKButton);
+		wxButton* pCancelButton = new wxButton(pPanel, wxID_CANCEL);
+		pBoxSizer->Add(pCancelButton);
+		pPanel->SetSizer(pBoxSizer);
+
+		pGridBagSizer->Add(pPanel, wxGBPosition(iRow++, 0), wxGBSpan(1, 2), wxALIGN_RIGHT);
+	}
+
+	m_bEnablePreviews = true;
+
+	UpdatePreviews();
+
+	m_pNotebook->Fit();
+	this->Fit();
+}
+
+void CConfigurationDialog::UpdatePreviews()
+{
+	if(!m_bEnablePreviews)
+		return;
+
+	CSteamID friendSteamID(1234, k_EUniversePublic, k_EAccountTypeIndividual);
+	CSteamID mySteamID(1337, k_EUniversePublic, k_EAccountTypeIndividual);
+
+	{
+		wxString preview = m_pFilenameInput->GetValue();
+		wxArrayString tags, replacements;
+
+		tags.Add(_T("{SteamID}"));		replacements.Add(wxString(friendSteamID.SteamRender(), wxConvUTF8));
+		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("{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()));
+
+		preview = CLogger::TagsReplace(preview, tags, replacements);
+		static const wxChar* invalidChars[] = {_T("\\"), _T("/"), _T(":"), _T("*"), _T("?"), _T("\""), _T("<"), _T(">"), _T("|")};
+
+		for(int i = 0; i < sizeof(invalidChars) / sizeof(*invalidChars); i++)
+		{
+			preview.Replace(invalidChars[i], m_pReplacementInput->GetValue());
+		}
+
+		m_pFilenamePreviewLabel->SetLabel(wxString::Format(_("Preview: %s"), preview));
+	}
+
+	{
+		wxString preview = m_pMessageInput->GetValue();
+		wxArrayString tags, replacements;
+
+		tags.Add(_T("{SteamID}"));		replacements.Add(wxString(friendSteamID.SteamRender(), wxConvUTF8));
+		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("{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!"));
+
+		preview = CLogger::TagsReplace(preview, tags, replacements);
+
+		m_pMessagePreviewLabel->SetLabel(wxString::Format(_("Preview: %s"), preview));
+	}
+
+	{
+		wxString preview = m_pEmoteInput->GetValue();
+		wxArrayString tags, replacements;
+
+		tags.Add(_T("{SteamID}"));		replacements.Add(wxString(friendSteamID.SteamRender(), wxConvUTF8));
+		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("{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."));
+
+		preview = CLogger::TagsReplace(preview, tags, replacements);
+
+		m_pEmotePreviewLabel->SetLabel(wxString::Format(_("Preview: %s"), preview));
+	}
+
+	m_pDatePreviewLabel->SetLabel(wxString::Format(_("Preview: %s"), wxDateTime::Now().Format(m_pDateInput->GetValue())));
+	m_pTimePreviewLabel->SetLabel(wxString::Format(_("Preview: %s"), wxDateTime::Now().Format(m_pTimeInput->GetValue())));
+
+	m_pFilenamePreviewLabel->Wrap(300);
+	m_pMessagePreviewLabel->Wrap(300);
+	m_pEmotePreviewLabel->Wrap(300);
+	m_pDatePreviewLabel->Wrap(300);
+	m_pTimePreviewLabel->Wrap(300);
+
+	m_pNotebook->Fit();
+	this->Fit();
+}
+
+void CConfigurationDialog::OnInputChange(wxCommandEvent& event)
+{
+	UpdatePreviews();
+}
+
+void CConfigurationDialog::OnTextURLEvent(wxTextUrlEvent& event)
+{
+	if(event.GetMouseEvent().LeftIsDown())
+	{
+		wxTextCtrl* pTextCtrl = (wxTextCtrl*)event.GetEventObject();
+		wxLaunchDefaultBrowser(pTextCtrl->GetRange(event.GetURLStart(), event.GetURLEnd()));
+	}
+}
+
+void CConfigurationDialog::OnButton(wxCommandEvent& event)
+{
+	if(event.GetId() == GetAffirmativeId())
+	{
+		m_config.Write(_T("LogDirectory"), m_pLogDirPicker->GetPath());
+		m_config.Write(_T("FilenameFormat"), m_pFilenameInput->GetValue());
+		m_config.Write(_T("ReplacementChar"), m_pReplacementInput->GetValue());
+		m_config.Write(_T("MessageFormat"), m_pMessageInput->GetValue());
+		m_config.Write(_T("EmoteFormat"), m_pEmoteInput->GetValue());
+		m_config.Write(_T("SeparationString"), m_pSeparationInput->GetValue());
+		m_config.Write(_T("DateFormat"), m_pDateInput->GetValue());
+		m_config.Write(_T("TimeFormat"), m_pTimeInput->GetValue());
+		m_config.Write(_T("Unstable"), pUnstableCheckBox->IsChecked());
+
+		CLogger* pLogger = wxGetApp().GetLogger();
+		pLogger->SetLogDirectory(m_pLogDirPicker->GetPath());
+		pLogger->SetFilenameFormat(m_pFilenameInput->GetValue());
+		pLogger->SetReplacementChar(m_pReplacementInput->GetValue()[0]);
+		pLogger->SetMessageFormat(m_pMessageInput->GetValue());
+		pLogger->SetEmoteFormat(m_pEmoteInput->GetValue());
+		pLogger->SetSeparationString(m_pSeparationInput->GetValue());
+		pLogger->SetDateFormat(m_pDateInput->GetValue());
+		pLogger->SetTimeFormat(m_pTimeInput->GetValue());
+		pLogger->UseClientInterfaces(pUnstableCheckBox->IsChecked());
+	}
+
+	this->Close();
+}

Projects/Chat Logger++/ConfigurationDialog.h

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#pragma once
+
+#include <wx/wx.h>
+#include <wx/msw/regconf.h>
+#include <wx/notebook.h>
+#include <wx/filepicker.h>
+
+class CConfigurationDialog : public wxDialog
+{
+public:
+	CConfigurationDialog();
+
+private:
+	void UpdatePreviews();
+
+	void OnInputChange(wxCommandEvent& event);
+	void OnTextURLEvent(wxTextUrlEvent& event);
+	void OnButton(wxCommandEvent& event);
+
+	bool m_bEnablePreviews;
+
+	wxConfig m_config;
+
+	wxNotebook* m_pNotebook;
+	wxPanel* m_pLoggingPanel;
+	wxPanel* m_pHelpPanel;
+
+	wxDirPickerCtrl *m_pLogDirPicker;
+
+	wxTextCtrl *m_pFilenameInput;
+	wxTextCtrl *m_pReplacementInput;
+	wxTextCtrl *m_pMessageInput;
+	wxTextCtrl *m_pEmoteInput;
+	wxTextCtrl *m_pSeparationInput;
+	wxTextCtrl *m_pDateInput;
+	wxTextCtrl *m_pTimeInput;
+
+	wxCheckBox* pUnstableCheckBox;
+
+	wxStaticText *m_pFilenamePreviewLabel;
+	wxStaticText *m_pMessagePreviewLabel;
+	wxStaticText *m_pEmotePreviewLabel;
+	wxStaticText *m_pDatePreviewLabel;
+	wxStaticText *m_pTimePreviewLabel;
+
+	DECLARE_EVENT_TABLE()
+};

Projects/Chat Logger++/Logger.cpp

+ďťż/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#include <wx/wx.h>
+#include <wx/msw/regconf.h>
+#include "Logger.h"
+#include "App.h"
+
+CLogger::CLogger() : wxThread(wxTHREAD_DETACHED), m_steamLoader(CSteamAPILoader::k_ESearchOrderSteamInstallFirst)
+{
+	m_hPipe = 0;
+	m_hUser = 0;
+	m_pSteamClient = NULL;
+	m_pSteamFriends = NULL;
+	m_pClientFriends = NULL;
+	m_pSteamUser = 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"));
+	config.Read(_T("ReplacementChar"), &m_replacementChar, _T("_"));
+	config.Read(_T("MessageFormat"), &m_messageFormat, _T("[{Time}] {Name}: {Message}"));
+	config.Read(_T("EmoteFormat"), &m_emoteFormat, _T("[{Time}] * {Name} {Message}"));
+	config.Read(_T("DateFormat"), &m_dateFormat, _T("%x"));
+	config.Read(_T("TimeFormat"), &m_timeFormat, _T("%X"));
+	config.Read(_T("SeparationString"), &m_separationString, _T("───────────────────"));
+	config.Read(_T("Unstable"), &m_bUseClientInterfaces, false);
+
+	this->Create();
+	this->Run();
+}
+
+CLogger::~CLogger()
+{
+	CleanupSteam();
+}
+
+void CLogger::SetLogDirectory(const wxString& path)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_logDirectory = path;
+}
+
+void CLogger::SetFilenameFormat(const wxString& format)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_filenameFormat = format;
+}
+
+void CLogger::SetReplacementChar(const wxString& c)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_replacementChar = c;
+}
+
+void CLogger::SetMessageFormat(const wxString& format)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_messageFormat = format;
+}
+
+void CLogger::SetEmoteFormat(const wxString& format)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_emoteFormat = format;
+}
+
+void CLogger::SetSeparationString(const wxString& string)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_separationString = string;
+}
+
+void CLogger::SetDateFormat(const wxString& format)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_dateFormat = format;
+}
+
+void CLogger::SetTimeFormat(const wxString& format)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	m_timeFormat = format;
+}
+
+bool CLogger::InitSteam()
+{
+	CreateInterfaceFn steam3Factory = m_steamLoader.GetSteam3Factory();
+	if(!steam3Factory)
+		return false;
+
+	m_pSteamClient = (ISteamClient012*)steam3Factory(STEAMCLIENT_INTERFACE_VERSION_012, NULL);
+	if(!m_pSteamClient)
+		return false;
+
+	m_hPipe = m_pSteamClient->CreateSteamPipe();
+	if(!m_hPipe || m_hPipe == -1)
+		return false;
+
+	// Steamworks likes to deadlock if we create the pipe while Steam is starting
+	// Which leads us to use this ugly hack.
+	m_pSteamClient->BReleaseSteamPipe(m_hPipe);
+	wxMilliSleep(5000);
+
+	m_hPipe = m_pSteamClient->CreateSteamPipe();
+	if(!m_hPipe || m_hPipe == -1)
+		return false;
+
+	m_hUser = m_pSteamClient->ConnectToGlobalUser(m_hPipe);
+	if(!m_hUser)
+		return false;
+
+	m_pSteamFriends = (ISteamFriends013*)m_pSteamClient->GetISteamFriends(m_hUser, m_hPipe, STEAMFRIENDS_INTERFACE_VERSION_013);
+	if(!m_pSteamFriends)
+		return false;
+
+	m_pSteamUser = (ISteamUser016*)m_pSteamClient->GetISteamUser(m_hUser, m_hPipe, STEAMUSER_INTERFACE_VERSION_016);
+	if(!m_pSteamUser)
+		return false;
+
+	if(m_bUseClientInterfaces)
+	{
+		IClientEngine* pClientEngine = (IClientEngine*)steam3Factory(CLIENTENGINE_INTERFACE_VERSION, NULL);
+		if(!pClientEngine)
+			return false;
+
+		m_pClientFriends = pClientEngine->GetIClientFriends(m_hUser, m_hPipe, CLIENTFRIENDS_INTERFACE_VERSION);
+		if(!m_pClientFriends)
+			return false;
+	}
+
+	wxGetApp().Message(_("Connected to Steam."));
+
+	return true;
+}
+
+void CLogger::CleanupSteam()
+{
+	if(m_pSteamClient)
+	{
+		if(m_hPipe && m_hPipe != -1)
+		{
+			if(m_hUser)
+			{
+				m_pSteamClient->ReleaseUser(m_hPipe, m_hUser);
+			}
+			m_pSteamClient->BReleaseSteamPipe(m_hPipe);
+		}
+	}
+
+	m_hPipe = 0;
+	m_hUser = 0;
+	m_pSteamClient = NULL;
+	m_pSteamFriends = NULL;
+	m_pClientFriends = NULL;
+	m_pSteamUser = NULL;
+}
+
+void CLogger::UseClientInterfaces(bool bUse)
+{
+	if(m_bUseClientInterfaces != bUse)
+	{
+		wxGetApp().Message(_("Reloading..."));
+
+		wxMutexLocker steamLock(m_steamLock);
+
+		CloseLogs();
+		m_bUseClientInterfaces = bUse;
+		CleanupSteam();
+	}
+}
+
+void CLogger::CallbackHandlerThread()
+{
+	for(; !this->TestDestroy(); this->Sleep(100))
+	{
+		wxMutexLocker steamLock(m_steamLock);
+
+		if(!m_hPipe || m_hPipe == -1)
+		{
+			if(!InitSteam())
+				continue;
+		}
+
+		static unsigned int i = 0;
+		if(++i % 50 == 0)
+		{
+			// This dummy call will trigger the IPCFailure_t callback if Steam has been closed.
+			if(m_pSteamUser)
+				m_pSteamUser->BLoggedOn();
+		}
+
+		CallbackMsg_t callbackMsg;
+		while(Steam_BGetCallback(m_hPipe, &callbackMsg))
+		{
+			switch(callbackMsg.m_iCallback)
+			{
+			case IPCFailure_t::k_iCallback:
+				{
+					IPCFailure_t* pIPCFailure = (IPCFailure_t*)callbackMsg.m_pubParam;
+					OnIPCFailure(pIPCFailure);
+					break;
+				}
+			case FriendChatMsg_t::k_iCallback:
+				{
+					FriendChatMsg_t* pFriendChatMsg = (FriendChatMsg_t*)callbackMsg.m_pubParam;
+					OnFriendChatMsg(pFriendChatMsg);
+					break;
+				}
+			case ChatRoomMsg_t::k_iCallback:
+				{
+					ChatRoomMsg_t* pChatRoomMsg = (ChatRoomMsg_t*)callbackMsg.m_pubParam;
+					OnChatRoomMsg(pChatRoomMsg);
+					break;
+				}
+			case ChatRoomDlgClose_t::k_iCallback:
+				{
+					ChatRoomDlgClose_t* pChatRoomDlgClose = (ChatRoomDlgClose_t*)callbackMsg.m_pubParam;
+					OnChatRoomDlgClose(pChatRoomDlgClose);
+					break;
+				}
+			}
+			Steam_FreeLastCallback(m_hPipe);
+		}
+	}
+}
+
+void CLogger::OnIPCFailure(IPCFailure_t* pIPCFailure)
+{
+	m_hPipe = 0;
+	m_hUser = 0;
+	m_pSteamClient = NULL;
+	m_pSteamFriends = NULL;
+	m_pSteamUser = NULL;
+
+	CloseLogs();
+
+	wxGetApp().Warning(_("Connection to Steam lost."));
+}
+
+void CLogger::OnFriendChatMsg(FriendChatMsg_t* pFriendChatMsg)
+{
+	if(!m_pSteamFriends)
+		return;
+
+	char szMessage[k_cchFriendChatMsgMax];
+	EChatEntryType eEntryType;
+	if(m_pSteamFriends->GetFriendMessage(pFriendChatMsg->m_ulFriendID, pFriendChatMsg->m_iChatID, szMessage, sizeof(szMessage), &eEntryType))
+	{
+		if(eEntryType == k_EChatEntryTypeChatMsg)
+		{
+			wxFFile* pFile = GetLogFile(pFriendChatMsg->m_ulFriendID);
+			if(pFile)
+			{
+				wxString message = m_messageFormat + _T("\n");
+				FormatMessage(message, pFriendChatMsg->m_ulSenderID, szMessage);
+				pFile->Write(message);
+			}
+		}
+		else if(eEntryType == k_EChatEntryTypeEmote)
+		{
+			wxFFile* pFile = GetLogFile(pFriendChatMsg->m_ulFriendID);
+			if(pFile)
+			{
+				wxString message = m_emoteFormat + _T("\n");
+				FormatMessage(message, pFriendChatMsg->m_ulSenderID, szMessage);
+				pFile->Write(message);
+			}
+		}
+	}
+}
+
+void CLogger::OnChatRoomMsg(ChatRoomMsg_t* pChatRoomMsg)
+{
+	if(!m_pClientFriends)
+		return;
+
+	char szMessage[k_cchFriendChatMsgMax];
+	EChatEntryType eEntryType;
+	CSteamID chatterID;
+	if(m_pClientFriends->GetChatRoomEntry(pChatRoomMsg->m_ulSteamIDChat, pChatRoomMsg->m_iChatID, &chatterID, szMessage, sizeof(szMessage), &eEntryType))
+	{
+		if(eEntryType == k_EChatEntryTypeChatMsg)
+		{
+			wxFFile* pFile = GetLogFile(pChatRoomMsg->m_ulSteamIDChat);
+			if(pFile)
+			{
+				wxString message = m_messageFormat + _T("\n");
+				FormatMessage(message, pChatRoomMsg->m_ulSteamIDUser, szMessage);
+				pFile->Write(message);
+			}
+		}
+		else if(eEntryType == k_EChatEntryTypeEmote)
+		{
+			wxFFile* pFile = GetLogFile(pChatRoomMsg->m_ulSteamIDChat);
+			if(pFile)
+			{
+				wxString message = m_emoteFormat + _T("\n");
+				FormatMessage(message, pChatRoomMsg->m_ulSteamIDUser, szMessage);
+				pFile->Write(message);
+			}
+		}
+	}
+}
+
+void CLogger::OnChatRoomDlgClose(ChatRoomDlgClose_t* pChatRoomDlgClose)
+{
+	wxFFile* pFile = GetLogFile(pChatRoomDlgClose->m_ulSteamIDChat, false);
+	if(pFile)
+	{
+		pFile->Write(m_separationString);
+
+		pFile->Write(_T("\n"));
+
+		pFile->Flush();
+		pFile->Close();
+		delete pFile;
+
+		m_logsOpened.erase(pChatRoomDlgClose->m_ulSteamIDChat);
+	}
+}
+
+
+wxString CLogger::TagsReplace(const wxString& string, const wxArrayString& tags, const wxArrayString& replacements)
+{
+	wxString ret;
+	for(size_t i = 0; i < string.length(); )
+	{
+		bool bFound = false;
+
+		if(string[i] == '{')
+		{
+			for(size_t j = 0; j < tags.Count(); j++)
+			{
+				wxString& tag = tags[j];
+				if(string.substr(i, tag.length()) == tag)
+				{
+					ret += replacements[j];
+					i += tag.length();
+
+					bFound = true;
+					break;
+				}
+			}
+		}
+
+		if(!bFound)
+		{
+			ret += string[i++];
+		}
+	}
+
+	return ret;
+}
+
+void CLogger::FormatFilename(wxString& string, CSteamID steamID)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	wxArrayString tags, replacements;
+
+	tags.Add(_T("{SteamID}"));		replacements.Add(wxString(steamID.SteamRender(), wxConvUTF8));
+	tags.Add(_T("{MySteamID}"));	replacements.Add(wxString(m_pSteamUser->GetSteamID().SteamRender(), wxConvUTF8));
+	tags.Add(_T("{SteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), steamID.ConvertToUint64()));
+	tags.Add(_T("{MySteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), m_pSteamUser->GetSteamID().ConvertToUint64()));
+
+	tags.Add(_T("{Name}"));
+	if(steamID.BIndividualAccount())
+	{
+		replacements.Add(wxString(m_pSteamFriends->GetFriendPersonaName(steamID), wxConvUTF8));
+	}
+	else
+	{
+		const char* cszChatRoomName = m_pClientFriends->GetChatRoomName(steamID);
+		if(*cszChatRoomName)
+		{
+			replacements.Add(wxString(cszChatRoomName, wxConvUTF8));
+		}
+		else
+		{
+			wxString name;
+			for(int i = 0; i < m_pSteamFriends->GetFriendCountFromSource(steamID); i++)
+			{
+				name += wxString(m_pSteamFriends->GetFriendPersonaName(m_pSteamFriends->GetFriendFromSourceByIndex(steamID, i)), wxConvUTF8) + _T(" + ");
+			}
+			name.RemoveLast(3);
+
+			replacements.Add(name);
+		}
+	}
+
+	tags.Add(_T("{Nickname}"));
+
+	if(steamID.BIndividualAccount())
+	{
+		const char* cszNickname = NULL;
+
+		if(m_pClientFriends)
+			cszNickname = m_pClientFriends->GetPlayerNickname(steamID);
+
+		if(cszNickname)
+		{
+			replacements.Add(wxString(cszNickname, wxConvUTF8));
+		}
+		else
+		{
+			replacements.Add(wxString(m_pSteamFriends->GetFriendPersonaName(steamID), wxConvUTF8));
+		}
+	}
+	else
+	{
+		replacements.Add(replacements.Last());
+	}
+
+	tags.Add(_T("{MyName}"));		replacements.Add(wxString(m_pSteamFriends->GetPersonaName(), wxConvUTF8));
+	tags.Add(_T("{Date}"));			replacements.Add(wxDateTime::Now().Format(m_dateFormat));
+	tags.Add(_T("{Time}"));			replacements.Add(wxDateTime::Now().Format(m_timeFormat));
+	tags.Add(_T("{UnixTime}"));		replacements.Add(wxString::Format(_T("%lld"), (long long)wxDateTime::GetTimeNow()));
+	
+	string = TagsReplace(string, tags, replacements);
+
+	static const wxChar* invalidChars[] = {_T("\\"), _T("/"), _T(":"), _T("*"), _T("?"), _T("\""), _T("<"), _T(">"), _T("|")};
+	for(int i = 0; i < sizeof(invalidChars) / sizeof(*invalidChars); i++)
+	{
+		string.Replace(invalidChars[i], m_replacementChar);
+	}
+}
+
+void CLogger::FormatMessage(wxString& string, CSteamID steamID, const char* cszMessage)
+{
+	wxMutexLocker configLock(m_configLock);
+
+	wxArrayString tags, replacements;
+
+	tags.Add(_T("{SteamID}"));		replacements.Add(wxString(steamID.SteamRender(), wxConvUTF8));
+	tags.Add(_T("{MySteamID}"));	replacements.Add(wxString(m_pSteamUser->GetSteamID().SteamRender(), wxConvUTF8));
+	tags.Add(_T("{SteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), steamID.ConvertToUint64()));
+	tags.Add(_T("{MySteamID64}"));	replacements.Add(wxString::Format(_T("%llu"), m_pSteamUser->GetSteamID().ConvertToUint64()));
+
+	tags.Add(_T("{Name}"));
+
+	wxString name = wxString(m_pSteamFriends->GetFriendPersonaName(steamID), wxConvUTF8);
+	const char* cszNickname = NULL;
+
+	if(m_pClientFriends)
+		cszNickname = m_pClientFriends->GetPlayerNickname(steamID);
+
+	if(cszNickname)
+	{
+		name += _T(" (") + wxString(cszNickname, wxConvUTF8) + _T(")");
+	}
+	replacements.Add(name);
+
+	tags.Add(_T("{MyName}"));		replacements.Add(wxString(m_pSteamFriends->GetPersonaName(), wxConvUTF8));
+	tags.Add(_T("{Date}"));			replacements.Add(wxDateTime::Now().Format(m_dateFormat));
+	tags.Add(_T("{Time}"));			replacements.Add(wxDateTime::Now().Format(m_timeFormat));
+	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(wxString(cszMessage, wxConvUTF8));
+	
+	string = TagsReplace(string, tags, replacements);
+}
+
+wxFFile* CLogger::GetLogFile(CSteamID steamID, bool bOpenIfNeeded)
+{
+	std::map<CSteamID, wxFFile*>::iterator it = m_logsOpened.find(steamID);
+	if(it != m_logsOpened.end())
+		return it->second;
+
+	if(!bOpenIfNeeded)
+		return NULL;
+
+	wxString filename = m_filenameFormat;
+	FormatFilename(filename, steamID);
+
+	wxLogNull logNull;
+
+	wxFFile* pFile = new wxFFile(m_logDirectory + _T("/") + filename, _T("at"));
+
+	if(pFile->IsOpened())
+	{
+		m_logsOpened[steamID] = pFile;
+
+		if(!pFile->Length())
+		{
+			// Add the UTF-8 BOM
+			pFile->Write("\xEF\xBB\xBF", 3);
+		}
+	}
+	else
+	{
+		wxGetApp().Warning(wxString::Format(_("Unable to open '%s' !"), filename));
+		delete pFile;
+		pFile = NULL;
+	}
+
+	return pFile;
+}
+
+void CLogger::CloseLogs()
+{
+	for(std::map<CSteamID, wxFFile*>::iterator it = m_logsOpened.begin(); it != m_logsOpened.end(); )
+	{
+		it->second->Flush();
+		it->second->Close();
+		delete it->second;
+
+		m_logsOpened.erase(it++);
+	}
+}
+
+CLogger::ExitCode CLogger::Entry()
+{
+	CallbackHandlerThread();
+
+	return NULL;
+}

Projects/Chat Logger++/Logger.h

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#pragma once
+
+#include <map>
+#include <wx/thread.h>
+#include <wx/ffile.h>
+#include "Steamworks.h"
+
+class CLogger : public wxThread
+{
+public:
+	CLogger();
+	~CLogger();
+
+	void SetLogDirectory(const wxString& path);
+	void SetFilenameFormat(const wxString& format);
+	void SetReplacementChar(const wxString& string);
+	void SetMessageFormat(const wxString& format);
+	void SetEmoteFormat(const wxString& format);
+	void SetSeparationString(const wxString& string);
+	void SetDateFormat(const wxString& format);
+	void SetTimeFormat(const wxString& format);
+
+	void UseClientInterfaces(bool bUse = false);
+
+	static wxString TagsReplace(const wxString& string, const wxArrayString& tags, const wxArrayString& replacements);
+
+private:
+	bool InitSteam();
+	void CleanupSteam();
+	void CallbackHandlerThread();
+
+	void OnIPCFailure(IPCFailure_t* pIPCFailure);
+	void OnFriendChatMsg(FriendChatMsg_t* pFriendChatMsg);
+	void OnChatRoomMsg(ChatRoomMsg_t* pChatRoomMsg);
+	void OnChatRoomDlgClose(ChatRoomDlgClose_t* pChatRoomDlgClose);
+
+	wxFFile* GetLogFile(CSteamID steamID, bool bOpenIfNeeded = true);
+	void CloseLogs();
+
+	virtual ExitCode Entry();
+
+	void FormatFilename(wxString& string, CSteamID steamID);
+	void FormatMessage(wxString& string, CSteamID steamID, const char* cszMessage);
+
+	bool m_bUseClientInterfaces;
+
+	CSteamAPILoader m_steamLoader;
+	HSteamPipe m_hPipe;
+	HSteamUser m_hUser;
+	ISteamClient012* m_pSteamClient;
+	ISteamUser016* m_pSteamUser;
+	ISteamFriends013* m_pSteamFriends;
+	IClientFriends* m_pClientFriends;
+
+	std::map<CSteamID, wxFFile*> m_logsOpened;
+
+	wxString m_logDirectory;
+	wxString m_filenameFormat;
+	wxString m_replacementChar;
+	wxString m_messageFormat;
+	wxString m_emoteFormat;
+	wxString m_separationString;
+	wxString m_dateFormat;
+	wxString m_timeFormat;
+	
+	wxMutex m_configLock;
+	wxMutex m_steamLock;
+};

Projects/Chat Logger++/TaskBarIcon.cpp

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#include "TaskBarIcon.h"
+#include <wx/wx.h>
+#include <Windows.h>
+
+void CTaskBarIcon::ShowMessage(const wxString& title, const wxString& message, EIcon eIcon)
+{
+	NOTIFYICONDATA nid;
+	memset(&nid, 0, sizeof(nid));
+
+	nid.cbSize              = sizeof(nid);
+
+	nid.hWnd                = (HWND) ((wxFrame*)this->m_win)->GetHWND();
+	nid.uID                 = 99;
+	nid.uFlags              = NIF_INFO;
+	nid.dwInfoFlags         = eIcon;
+	wcscpy(nid.szInfoTitle, title.Mid(0, (sizeof(nid.szInfoTitle) / sizeof(*nid.szInfoTitle)) - 1).wc_str());
+	wcscpy(nid.szInfo, message.Mid(0, (sizeof(nid.szInfo) / sizeof(*nid.szInfo)) - 1).wc_str());
+
+	Shell_NotifyIcon(NIM_MODIFY, &nid);
+}

Projects/Chat Logger++/TaskBarIcon.h

+/*
+	This file is a part of "Chat Logger++"
+	Š2k12, Didrole
+	
+	License : Public domain
+*/
+
+#pragma once
+
+#include <wx/taskbar.h>
+
+
+class CTaskBarIcon : public wxTaskBarIcon
+{
+public:
+
+	enum EIcon
+	{
+		k_EIconNone = 0,
+		k_EIconInfo,
+		k_EIconWarning,
+		k_EIconError,
+	};
+	void ShowMessage(const wxString& title, const wxString& message, EIcon eIcon = k_EIconInfo);
+};

Projects/Chat Logger++/app.ico

Added
New image

Projects/Chat Logger++/resource.rc

+#include "afxres.h"
+
+AppIcon               ICON                    "app.ico"
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "000904b0"
+        BEGIN
+            VALUE "FileVersion", "1, 0, 0, 0"
+            VALUE "LegalCopyright", "Š2k12, Didrole"
+            VALUE "ProductName", "Chat Logger++"
+            VALUE "ProductVersion", "1, 0, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x9, 1200
+    END
+END