/*
 * Emunation Plugin for MD-API
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#include "main.h"
#include "EmuNation_Impl.h"
#include "hklm.h"
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>
#include "misc.h"
#include "common.h"
#include "sc.h"
#include "smartcard.h"
#include <process.h>
#include ".\\dvbparser\\dvbsectiondata.h"
#include ".\\DVBCore\\DVBCoreWrapper.h"

//#define THREADED_STATE_CONTROL
#define THREADED_START_FILTER
#define THREADED_DATA_FLOW
//#define MAIN_LOOP_THREADED
#define INI_FILE_NAME "EmuNation.ini"
#define ACT_START_FILTER 0
#define ACT_STOP_FILTER 1

//Prototypes
BOOL APIENTRY DllMonitorDlgFunc(HWND hDlg,UINT message,UINT wParam,LONG lParam);
BOOL APIENTRY DllAboutDlgFunc(HWND hDlg,UINT message,UINT wParam,LONG lParam);
BOOL APIENTRY DllDecryptDlgFunc(HWND hDlg,UINT message,UINT wParam,LONG lParam);
void APIENTRY GetDlgPosition(HWND hDlg, LPCTSTR lpszEntry);
void APIENTRY SaveDlgPosition(HWND hDlg, LPCTSTR lpszEntry);

typedef struct __FilterArgs
{
	int action;
	CAPI_Impl* owner;
	TSTART_FILTER* sf;
	int filterid;
} FilterArgs;

FilterArgs fargs;

bool g_BeepOnNewKey;
bool g_LogE3;
bool g_north_america;

void STATE_CONTROL(void* impl);
void DATA_FLOW(void* impl);

#define LOG_DEBUG_PLG(X) Log("%s Func: %s Line:%d\n",X,__FUNCTION__,__LINE__);
DllClass m_DLLInstance;
CAPI_Impl *g_API_Impl=&m_DLLInstance;

CHKLM hklm;
CString sSection(EMUNATION_PLG_NAME);
RECT rc;
int LeftPos, TopPos = 0;

char *auxAddr = "";
char *auxPass = "";
int auxPort = 0;
int emm_0101_pid_hex = 0;
int emm_0901_pid_hex = 0;
int m_Start_0101_EmmOnChange=0;
int m_Start_0901_EmmOnChange=0;
int m_Stop_0101_EmmOnChange=0;
int m_Stop_0901_EmmOnChange=0;
int m_keep_old_keys=0;
long emmTimeout = 0;
int myDataLen = 184;
bool doOnce = true;
bool GrabOne = false;

int NADCWTweak = 0;
int ProviderID = 0;
bool StartEMMrx = false;

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
inline void OpenConsole(void)
{
    int hCrt;
    FILE *hf = 0;
	BOOL bSuccess;
	char ConsoleTitle[128];
	HWND hDlg;

	if ( AllocConsole() ) {
	// Error Console
		HANDLE hErrorConsole = GetStdHandle( STD_ERROR_HANDLE );
		hf=0;
		hCrt = _open_osfhandle( (long)hErrorConsole, _O_TEXT );
		if (hCrt!=-1) hf = _fdopen(hCrt, "w");
		if (hf!=0) *stderr = *hf;
		if (hCrt==-1 || hf==0 || 0!=setvbuf( stderr, NULL, _IONBF, 0 )) {
//			("reroute stderr failed in SetupConsole");
            return;
		}
	// Output Console
		HANDLE hOutputConsole = GetStdHandle( STD_OUTPUT_HANDLE );
		hf=0;
		hCrt = _open_osfhandle( (long)hOutputConsole, _O_TEXT );
		if (hCrt!=-1) hf = _fdopen(hCrt, "w");
		if (hf!=0) *stdout = *hf;
		if (hCrt==-1 || hf==0 || 0!=setvbuf( stdout, NULL, _IONBF, 0 )) {
//			("unable to mount reroute stdout");
            return;
		}

	//Console Buffer Size {Width, Height)
		const COORD screenSz = {80, 5000};
		SetConsoleScreenBufferSize(hOutputConsole, screenSz);

	//Console Title
		sprintf(ConsoleTitle,"%s Console",EMUNATION_PLG_NAME);
		bSuccess = SetConsoleTitle(ConsoleTitle);

		hDlg = FindWindow(NULL,ConsoleTitle);
		if (hDlg) {
			m_DLLInstance.m_hwnd_console=hDlg;
			GetDlgPosition(hDlg, "ConsoleDlg");
			SetWindowPos(hDlg,HWND_TOP,LeftPos,TopPos,0,0,
				SWP_NOSIZE /*|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE */);
		}

    // Change the console window size:
//		SMALL_RECT windowSize = {LeftPos, TopPos, 80, 30};
//		bSuccess = SetConsoleWindowInfo(hOutputConsole, TRUE, &windowSize);

    return;
   }
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CloseConsole(void)
{
	BOOL bSuccess;
	char ConsoleTitle[128];
	HWND hDlg;

//Console Title
	sprintf(ConsoleTitle,"%s Console",EMUNATION_PLG_NAME);
	bSuccess = SetConsoleTitle(ConsoleTitle);

	hDlg = FindWindow(NULL,ConsoleTitle);
	SaveDlgPosition(hDlg, "ConsoleDlg");

	FreeConsole();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
DllClass::DllClass()
{
	m_prevecmdata=NULL;
	m_hwnd_monitor=NULL;
	m_hwnd_console=NULL;
	remove("emulog.txt");
	m_flow=0;
	filterCount=0;
	ThreadState=true;
	PluginEvent=EVENT_IDLE;
	IsInitialized=false;
	DVBCoreFlag=false;
	filterCamID=false;
	filterGroup=false;
	strcpy(m_infotext,"No information provided.");
	strcpy(m_keystr,"No keys");

	m_ecm_active = false;
	m_emm_active = false;
	m_isdatafilter = false;	
	m_isecmfilter = false;
	m_isemmfilter = false;
	m_ispmtfilter = false;	
	m_plg_active = false;
	firstEMMpacket = false;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
DllClass::~DllClass ()
{

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::OnDVBCoreInit(HWND Handle)
{
	//printf("\n\n**** OnDVBCoreInit\n\n");
	m_MultiDecWindow=Handle;
	DVBCoreFlag=true;
	OnEmunationInit();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::OnEmunationInit()
{
	BadKey = true;
	sprintf(INI_FILE,"%s\\%s",dlldirPath,INI_FILE_NAME);
	InitializeOptions();

	//Menu Initialization
	InitializeMenu();

    //Set the name of the Company
	hklm.SetRegistryKey("UICN - #DVB");
	hklm.SetRegistryKey(IDS_APP_TITLE);
	hklm.SetProductNameRegistryKey("UICN - #DVB");

	//System
	if (m_have_console) OpenConsole();
	if (m_load_monitor) ShowDialog(IDD_DLG_MONITOR);

	m_provs.Init(dlldirPath); 

	IsInitialized=true;
	if (!m_plg_active) return;
	ActivatePlugin();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::ActivatePlugin()
{
	keys.EMMptr = 0;
	keys.Load(dlldirPath);
	cSystems::Init(dlldirPath);
	filemaps.SetCfgDir(dlldirPath);
	if(!cSoftCAM::Load(dlldirPath)) printf("!!ERROR!!");

	if (m_use_aux_emu == false) {
		smartcards.AddPort(DEFAULT_PORT);
	}
	smartcards.LaunchWatcher();

	//ECM Filter Initialization
	ECM_FilterInit();
	//EMM Filter Initialization
	EMM_FilterInit();
	//DATA filter
	DATA_filterInit();

    //	  Log("API Version:%s\n",Api_Version);
	//	  Log("MDWnd %p\n",MDWnd);
#ifdef THREADED_STATE_CONTROL
	_beginthread( STATE_CONTROL, 0, this );
#endif

#ifdef THREADED_DATA_FLOW
	_beginthread( DATA_FLOW, 0, this );
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::ECM_FilterInit()
{
	if(!m_isecmfilter){
		//d(printf("=> DllClass::ECM_FilterInit\n"));
		m_ecmfilter.DLL_ID=m_MultiDec_DLL_ID;
		m_ecmfilter.Filter_ID=ECM_FILTER_ID;
		m_ecmfilter.Irq_Call_Adresse=0;
		strcpy((char*)m_ecmfilter.Name,ECM_FILTER_NAME);
		m_ecmfilter.Running_ID=-1;
		m_ecmfilter.m_ExtSize=0;
		m_ecmfilter.m_SectionMask=0;
		m_ecmfilter.m_SectionData=0;
		m_ecmfilter.m_185Mode=false;
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::EMM_FilterInit()
{
	if(!m_isemmfilter){
		//d(printf("=> DllClass::EMM_FilterInit\n"));
		m_emmfilter.DLL_ID=m_MultiDec_DLL_ID;
		m_emmfilter.Filter_ID=EMM_FILTER_ID;
		m_emmfilter.Irq_Call_Adresse=0;
		strcpy((char*)m_emmfilter.Name,EMM_FILTER_NAME);
		m_emmfilter.Running_ID=-2;
		m_emmfilter.m_ExtSize=0;
		m_emmfilter.m_SectionMask=0;
		m_emmfilter.m_SectionData=0;
		m_emmfilter.m_185Mode=false;
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::DATA_filterInit()
{
	if(!m_isdatafilter){
		//d(printf("=> DllClass::DATA_filterInit\n"));
		m_datafilter.DLL_ID=m_MultiDec_DLL_ID;
		m_datafilter.Filter_ID=DATA_FILTER_ID;
		m_datafilter.Irq_Call_Adresse=0;
		strcpy((char*)m_datafilter.Name,"PMT FILTER");
		m_datafilter.Running_ID=-3;
		m_datafilter.m_ExtSize=0;
		m_datafilter.m_SectionMask=0;
		m_datafilter.m_SectionData=0;
		m_datafilter.m_185Mode=false;
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
HINSTANCE DllClass::GetDLLInstance()
{
	return m_hDLL;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL DllClass::DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
{
	CAPI_Impl::DllMain(hDLL,dwReason,lpReserved);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void FILTER_CONTROL (void* args)
{
	int ret;
	FilterArgs* f=(FilterArgs*) args;
	if (f->action == ACT_START_FILTER)
	{
		ret = f->owner->Start_Filter(f->sf);
//		d(printf("*** FILTER_CONTROL called ... CAPI_impl Start_Filter returns %d\n", ret))
	}
	else
	{
		f->owner->Stop_Filter(f->filterid);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int DllClass::Start_Filter(struct TSTART_FILTER *sf)
{
//	d(printf("*** DllClass::Start_Filter called ... DVBCoreFlag = %d\n", DVBCoreFlag))
	if (!DVBCoreFlag)
	{
#ifdef THREADED_START_FILTER
		//	  Log("Threaded Start %X\n",sf->Filter_ID);
		fargs.action=ACT_START_FILTER;
		fargs.owner=this;
		fargs.sf=sf;
		fargs.filterid=0;
		_beginthread( FILTER_CONTROL, 0, &fargs );
		Sleep(100);
		return 1;
#else

		return CAPI_Impl::Start_Filter(sf);
#endif
	}

	return DVBCORE_Start_Filter(sf);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int DllClass::Stop_Filter(int filterid)
{
//	d(printf("*** DllClass::Stop_Filter called ... DVBCoreFlag = %d\n", DVBCoreFlag))
	if (!DVBCoreFlag)
	{
#ifdef THREADED_START_FILTER
		fargs.action=ACT_STOP_FILTER;
		fargs.owner=this;
		fargs.sf=NULL;
		fargs.filterid=filterid;
		_beginthread( FILTER_CONTROL, 0, &fargs );
		Sleep(100);
		return 1;
#else
		return CAPI_Impl::Stop_Filter(filterid);
#endif
	}
	return DVBCORE_Stop_Filter(filterid);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::On_Send_Dll_ID_Name(char *Name)
{
	strcpy(Name,EMUNATION_PLG_NAME);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::DVBCORE_Stop_Filter(int flt_id)
{
	DWORD* Filter;
	if(flt_id<=0) return false;
	if (flt_id == DVBCORE_ECM_FILTER)
	{
		Filter = &DVBCORE_ECM_FILTER;
		m_ecmfilter.Running_ID = 0;

	}
	else
		if (flt_id == DVBCORE_EMM_FILTER)
		{
			Filter = &DVBCORE_EMM_FILTER;
			m_emmfilter.Running_ID = 0;
		}
		else
			if (flt_id == DVBCORE_PMT_FILTER)
			{
				Filter = &DVBCORE_PMT_FILTER;
				m_datafilter.Running_ID = 0;
			}

			_DVBDelFilter(flt_id);
			*Filter=0;
			return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::DVBCORE_Start_Filter(struct TSTART_FILTER *sf)
{
	int flt_id = sf->Filter_ID;

	if (flt_id == ECM_FILTER_ID) {
		DVBCORE_ECM_FILTER = _DVBAddFilter(sf->Pid,(LPCB_STREAMHOOK)STREAM_FILTER,FT_STREAMING_FILTER,NULL,NULL,0);
		sf->Running_ID = DVBCORE_ECM_FILTER;
		//Log("\n\nStart ECM: %d \n\n",DVBCORE_ECM_FILTER);
	} else if (flt_id == EMM_FILTER_ID) {
		DVBCORE_EMM_FILTER = _DVBAddFilter(sf->Pid,(LPCB_STREAMHOOK)STREAM_FILTER,FT_STREAMING_FILTER,NULL,NULL,0);
		sf->Running_ID = DVBCORE_EMM_FILTER;
		//Log("\n\nStart EMM: %d \n\n",DVBCORE_EMM_FILTER);
//		d(printf("*** DVBCORE_Start_Filter called ... Start EMM: flt_id = %d DVBCORE_EMM_FILTER = %d\n", flt_id, DVBCORE_EMM_FILTER))
	} else if (flt_id == DATA_FILTER_ID) {
		DVBCORE_PMT_FILTER = _DVBAddFilter(sf->Pid,(LPCB_STREAMHOOK)STREAM_FILTER,FT_STREAMING_FILTER,NULL,NULL,0);
		sf->Running_ID = DVBCORE_PMT_FILTER;
		//Log("\n\nStart PMT: %d \n\n",DVBCORE_PMT_FILTER);
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StartDataFilter_TH()
{
	m_datafilter.Pid=m_curprg.PMT_pid;
	//return Start_Filter(&m_datafilter);
	if ( Start_Filter(&m_datafilter) ) {
		//d(printf("Sucessfully started data filter\n"))
		m_isdatafilter = true;
	} else {
		d(printf("Failed started data filter\n"))
		m_isdatafilter = false;
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StopDataFilter_TH()
{
	//return Stop_Filter(m_datafilter.Running_ID);
	if (m_isdatafilter) {
		Stop_Filter(m_datafilter.Running_ID);
		m_isdatafilter=false;
		//d(printf("Sucessfully stopped data filter\n"))
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::On_Exit(HINSTANCE MDInstance, HWND MDWnd, BOOL Log_Set)
{
	if (m_hwnd_console) {
		SaveDlgPosition(m_hwnd_console, "ConsoleDlg");
		CloseConsole();
	}
	if (m_hwnd_monitor) {
		SaveDlgPosition(m_hwnd_monitor, "MonitorDlg");
		EndDialog(m_hwnd_monitor,0);
	}
	StopEMMFilter();
	StopECMFilter();
	SaveOptions();
	ThreadState=false;
	//	  Log("On Exit\n");
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::On_Filter_Close(unsigned int FilterOffset )
{
	// Log("OnFilterClose: %d\n",FilterOffset);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::Log(const char* format,...)
{
	char in_buffer[256];
	char out_buffer[512];
	char time_str[20];
	char LogPath[_MAX_PATH];

	sprintf(LogPath,"%s\\%s",GetLogPath(),"emulog.txt");
	FILE* f=fopen(LogPath,"at");
	va_list argptr;
	va_start( argptr, format );
	vsprintf(in_buffer,format,argptr);
	va_end(argptr);
	_strtime(time_str);
	sprintf(out_buffer,"[%s]%s",time_str,in_buffer);
	fprintf(f,out_buffer);
	fclose(f);

	PushMessage(MSG_MESSAGE,out_buffer);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
char* DllClass::GetLogPath()
{
	char LogPath[_MAX_PATH];
	SYSTEMTIME st;
//	GetSystemTime(&st);	//Retrieves time based on coordinated universal time (UTC).
	GetLocalTime(&st);	//Retrieves the current local date and time.

	//**********************************************************
	//Make sure LogPath exists
	if (!strlen(strLogPath)) {
		sprintf(strLogPath,"%s\\Logs",curPath);
	}
	CreateDir(strLogPath);

	sprintf(LogPath,"%s\\%0d-%0d-%0d",strLogPath,st.wYear,st.wMonth,st.wDay);
	CreateDir(LogPath);
	//**********************************************************

	return LogPath;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::LogToFile(char* filename, bool AddTime, const char* format,...)
{
	char in_buffer[256];
	char out_buffer[512];
	char LogPath[_MAX_PATH];
	char time_str[20];

	sprintf(LogPath,"%s\\%s",GetLogPath(),filename);
	FILE* keyfile = fopen(LogPath,"a");
	if (keyfile) {
		va_list argptr;
		va_start( argptr, format );
		vsprintf(in_buffer,format,argptr);
		va_end(argptr);
		if (AddTime) {
			_strtime(time_str);
			sprintf(out_buffer,"[%s] %s",time_str,in_buffer);
		} else {
			sprintf(out_buffer,"%s",in_buffer);
		}
		fprintf(keyfile,out_buffer);
		fclose(keyfile);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//This function handles menu calls....
//------------------------------------------------------------------------------
void DllClass::On_Menu_Select(unsigned int MenuID )
{
	HMENU EmuMenu;
	if(m_DLLInstance.DVBCoreFlag)
	{
		EmuMenu = GetMenu(m_hwnd_monitor);
	}
	else
	{
		EmuMenu = GetMenu(m_MultiDecWindow);
	}

	switch(MenuID)
	{
	case IDM_ACTIVE:
		m_plg_active=!m_plg_active;
		CheckMenuItem(EmuMenu,IDM_ACTIVE,m_plg_active? MF_CHECKED:MF_UNCHECKED);
		if(m_plg_active){
			WritePrivateProfileInt("EmuNation","Active",1,INI_FILE);
			ActivatePlugin();
			StartDataFilter();
			StartECMFilter(&m_curprg);
			StartEMMFilter(&m_curprg);
		} else {
			WritePrivateProfileInt("EmuNation","Active",0,INI_FILE);
			StopECMFilter();
			StopEMMFilter();
			ClearAllData();
		}
		break;

	case IDM_ECM_ACTIVE:
		m_ecm_active=!m_ecm_active;
		CheckMenuItem(EmuMenu,IDM_ECM_ACTIVE,m_ecm_active? MF_CHECKED:MF_UNCHECKED);
		if (m_ecm_active) {
			WritePrivateProfileInt("EmuNation","ProcessECM",1,INI_FILE);
			StartECMFilter(&m_curprg);
		} else {
			WritePrivateProfileInt("EmuNation","ProcessECM",0,INI_FILE);
			StopECMFilter();
			ClearAllData();
		}
		break;

	case IDM_EMM_ACTIVE:
		m_emm_active=!m_emm_active;
		CheckMenuItem(EmuMenu,IDM_EMM_ACTIVE,m_emm_active? MF_CHECKED:MF_UNCHECKED);
		if (m_emm_active) {
			WritePrivateProfileInt("EmuNation","ProcessEMM",1,INI_FILE);
			StartEMMFilter(&m_curprg);
		} else {
			WritePrivateProfileInt("EmuNation","ProcessEMM",0,INI_FILE);
			StopEMMFilter();
			ClearAllData();
		}
		break;

	case IDM_BEEP_ON_KEY:
		m_beep_on_key=!m_beep_on_key;
		CheckMenuItem(EmuMenu,IDM_BEEP_ON_KEY,m_beep_on_key? MF_CHECKED:MF_UNCHECKED);
		if (m_beep_on_key) {
			WritePrivateProfileInt("Menu Options","BeepOnKeyFind",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Menu Options","BeepOnKeyFind",0,INI_FILE);
		}
		g_BeepOnNewKey = m_beep_on_key;
		break;

	case IDM_NORTH_AMERICA:
		m_north_america=!m_north_america;
		CheckMenuItem(EmuMenu,IDM_NORTH_AMERICA,m_north_america? MF_CHECKED:MF_UNCHECKED);
		if (m_north_america) {
			WritePrivateProfileInt("EmuNation","NorthAmerica",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","NorthAmerica",0,INI_FILE);
		}
		g_north_america = m_north_america;
		break;

	case IDM_SHOW:
		ShowDialog(IDD_DLG_MONITOR);
		break;

	case IDM_USE_MAX_PARSER:
		m_use_max_parser=!m_use_max_parser;
		CheckMenuItem(EmuMenu,IDM_USE_MAX_PARSER,m_use_max_parser? MF_CHECKED:MF_UNCHECKED);
		if (m_use_max_parser) {
			WritePrivateProfileInt("EmuNation","UseMaxParser",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","UseMaxParser",0,INI_FILE);
		}
		break;

	case IDM_HIDE_BAD_MESSAGE:
		m_hide_bad_data=!m_hide_bad_data;
		CheckMenuItem(EmuMenu,IDM_HIDE_BAD_MESSAGE,m_hide_bad_data? MF_CHECKED:MF_UNCHECKED);
		if (m_hide_bad_data) {
			WritePrivateProfileInt("EmuNation","HideBadData",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","HideBadData",0,INI_FILE);
		}
		break;

	case IDM_PROCESS_EMM_S:
		m_process_emm_s=!m_process_emm_s;
		CheckMenuItem(EmuMenu,IDM_PROCESS_EMM_S,m_process_emm_s? MF_CHECKED:MF_UNCHECKED);
		if (m_process_emm_s) {
			WritePrivateProfileInt("EmuNation","ProcessEmmS",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","ProcessEmmS",0,INI_FILE);
		}
		break;

	case IDM_LOAD_MONITOR:
		m_load_monitor=!m_load_monitor;
		CheckMenuItem(EmuMenu,IDM_LOAD_MONITOR,m_load_monitor? MF_CHECKED:MF_UNCHECKED);
		if (m_load_monitor) {
			WritePrivateProfileInt("EmuNation","LoadMonitorOnStart",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","LoadMonitorOnStart",0,INI_FILE);
		}
		break;

	case IDM_SHOW_CONSOLE:
		m_have_console=!m_have_console;
		CheckMenuItem(EmuMenu,IDM_SHOW_CONSOLE,m_have_console? MF_CHECKED:MF_UNCHECKED);
		if (m_have_console) {
			OpenConsole();
			WritePrivateProfileInt("EmuNation","ShowConsole",1,INI_FILE);
		} else {
			CloseConsole();
			WritePrivateProfileInt("EmuNation","ShowConsole",0,INI_FILE);
		}
		break;

	case IDM_SHOW_DISASSEMBLY_BASIC:
		m_show_diss=0;
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_BASIC,MF_CHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_DEBUG,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_VERBOSE,MF_UNCHECKED);
		WritePrivateProfileInt("Menu Options","ShowDisassembly",0,INI_FILE);
		break;
	case IDM_SHOW_DISASSEMBLY_DEBUG:
		m_show_diss=1;
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_BASIC,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_DEBUG,MF_CHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_VERBOSE,MF_UNCHECKED);
		WritePrivateProfileInt("Menu Options","ShowDisassembly",1,INI_FILE);
		break;
	case IDM_SHOW_DISASSEMBLY_VERBOSE:
		m_show_diss=2;
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_BASIC,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_DEBUG,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_VERBOSE,MF_CHECKED);
		WritePrivateProfileInt("Menu Options","ShowDisassembly",2,INI_FILE);
		break;

	case IDM_ABOUT:
		ShowDialog(IDD_DLG_ABOUT);
		break;

		///Logger Menu Selections
	case IDM_LOGECMTEXT:
		CheckMenuItem(EmuMenu,IDM_LOGECMTEXT,m_logger.ToggleActive(cLogger::ECM_Text)? MF_CHECKED:MF_UNCHECKED);
		break;

	case IDM_LOGECMBIN:
		CheckMenuItem(EmuMenu,IDM_LOGECMBIN,m_logger.ToggleActive(cLogger::ECM_Bin)? MF_CHECKED:MF_UNCHECKED);
		break;

	case IDM_LOGEMMTEXT:
		CheckMenuItem(EmuMenu,IDM_LOGEMMTEXT,m_logger.ToggleActive(cLogger::EMM_Text)? MF_CHECKED:MF_UNCHECKED);
		break;

	case IDM_LOGEMMBIN:
		CheckMenuItem(EmuMenu,IDM_LOGEMMBIN, m_logger.ToggleActive(cLogger::EMM_Bin)? MF_CHECKED:MF_UNCHECKED);
		break;

	case IDM_LOGCW:
		CheckMenuItem(EmuMenu,IDM_LOGCW,m_logger.ToggleActive(cLogger::DCW)? MF_CHECKED:MF_UNCHECKED);
		break;

	case IDM_LOGE3:
		m_loge3=!m_loge3;
		CheckMenuItem(EmuMenu,IDM_LOGE3,m_loge3? MF_CHECKED:MF_UNCHECKED);
		if (m_loge3) {
			WritePrivateProfileInt("Logger","LogE3",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Logger","LogE3",0,INI_FILE);
		}
		g_LogE3 = m_loge3;
		break;

	case IDM_KEEP_KEYS:
		m_keep_old_keys=!m_keep_old_keys;
		CheckMenuItem(EmuMenu,IDM_KEEP_KEYS,m_keep_old_keys? MF_CHECKED:MF_UNCHECKED);
		if (m_keep_old_keys) {
			WritePrivateProfileInt("EmuNation","KeepOldKeys",1,INI_FILE);
		} else {
			WritePrivateProfileInt("EmuNation","KeepOldKeys",0,INI_FILE);
		}
		break;

	case IDM_LOG_EMM_G:
		m_log_emm_g=!m_log_emm_g;
		CheckMenuItem(EmuMenu,IDM_LOG_EMM_G,m_log_emm_g? MF_CHECKED:MF_UNCHECKED);
		if (m_log_emm_g) {
			WritePrivateProfileInt("Logger","LogEMM-G",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Logger","LogEMM-G",0,INI_FILE);
		}
		break;

	case IDM_LOG_MONITOR:
		m_log_monitor=!m_log_monitor;
		CheckMenuItem(EmuMenu,IDM_LOG_MONITOR,m_log_monitor? MF_CHECKED:MF_UNCHECKED);
		if (m_log_monitor) {
			WritePrivateProfileInt("Logger","LogMonitor",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Logger","LogMonitor",0,INI_FILE);
		}
		break;
	case IDM_LOG_CONSOLE:
		m_log_console=!m_log_console;
		CheckMenuItem(EmuMenu,IDM_LOG_CONSOLE,m_log_console? MF_CHECKED:MF_UNCHECKED);
		if (m_log_console) {
			WritePrivateProfileInt("Logger","LogConsole",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Logger","LogConsole",0,INI_FILE);
		}
		break;

	case IDM_LOGEMM_OFF:				//(0) Do Not Log
		m_log_emm_by=0;
		WritePrivateProfileInt("Logger","LogEMM-S",0,INI_FILE);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_OFF,MF_CHECKED);				//<====
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYSINGLEFILE,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYTWOFILES,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYCAMID,MF_UNCHECKED);
		break;
	case IDM_LOGEMM_LOGBYSINGLEFILE:	//(1) Log By Single File
		m_log_emm_by=1;
		WritePrivateProfileInt("Logger","LogEMM-S",1,INI_FILE);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_OFF,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYSINGLEFILE,MF_CHECKED);	//<====
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYTWOFILES,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYCAMID,MF_UNCHECKED);
		break;
	case IDM_LOGEMM_LOGBYTWOFILES:		//(2) Log by Group/Specific
		m_log_emm_by=2;
		WritePrivateProfileInt("Logger","LogEMM-S",2,INI_FILE);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_OFF,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYSINGLEFILE,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYTWOFILES,MF_CHECKED);		//<====
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYCAMID,MF_UNCHECKED);
		break;
	case IDM_LOGEMM_LOGBYCAMID:			//(3) Log by Cam ID
		m_log_emm_by=3;
		WritePrivateProfileInt("Logger","LogEMM-S",3,INI_FILE);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_OFF,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYSINGLEFILE,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYTWOFILES,MF_UNCHECKED);
		CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYCAMID,MF_CHECKED);		//<====
		break;
	case IDM_LOGCAMID:					// Suppress Raw Data
		m_log_camid=!m_log_camid;
		CheckMenuItem(EmuMenu,IDM_LOGCAMID,m_log_camid? MF_CHECKED:MF_UNCHECKED);
		if (m_log_camid) {
			WritePrivateProfileInt("Logger","LogRawEMM-S",1,INI_FILE);
		} else {
			WritePrivateProfileInt("Logger","LogRawEMM-S",0,INI_FILE);
		}
		break;

	case IDM_LOADKEYS:
		keys.EMMptr = 0;
		BadKey = true;
		keys.Load(dlldirPath);
		break;
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::On_Hot_Key()
{
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
//#define TOLOG
void DllClass::to_log(char *x)
{
#ifdef TOLOG
	char LogPath[_MAX_PATH];
	sprintf(LogPath,"%s\\%s",GetLogPath(),"emunation.txt");
	FILE *f=fopen(LogPath,"at");
	if(f)
	{
		fprintf(f,"%s\n",x);
		fclose(f);
	}
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::On_Start(HINSTANCE MDInstance, HWND MDWnd, BOOL Log_Set, int DLL_ID, char *My_Hot_Key,char *Api_Version, int *Keep_me_running )
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // Prevent failed ASSERT

	if(IsInitialized)return;
	//ma_hDll = GetModuleHandle("EmuNation.dll");
	CAPI_Impl::On_Start(MDInstance,MDWnd,Log_Set,DLL_ID,My_Hot_Key,Api_Version,Keep_me_running);
	//State variables..
	OnEmunationInit();
	*Keep_me_running=1;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::ShowDialog(int dlgID)
{
	switch (dlgID){
		case IDD_DLG_ABOUT:
			DialogBox(m_hDLL,MAKEINTRESOURCE(dlgID),m_MultiDecWindow,DllAboutDlgFunc);
			break;

		case IDD_DLG_MONITOR:
			if(!m_hwnd_monitor)
				CreateDialog(m_hDLL,MAKEINTRESOURCE(dlgID),DVBCoreFlag?NULL:m_MultiDecWindow,DllMonitorDlgFunc);
			if (m_hwnd_monitor != NULL) {
				/* Dialog box already exists, display it */
				ShowWindow (m_hwnd_monitor, SW_SHOWDEFAULT);

				/* User has lost the dialog.  Show them where it is. */
				SetForegroundWindow (m_hwnd_monitor);
			}
			break;
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::PushMessage(int msgtype,char* msg)
{
	switch(msgtype)
	{
	case MSG_INFO:
		strncpy(m_infotext,msg,sizeof(m_infotext));
		if(m_hwnd_monitor){
			SetDlgItemText(m_hwnd_monitor,IDC_SYS,m_infotext);
		}

		break;
	case MSG_CONSOLE:
// ******************************************
// log Monitor Messages
// ******************************************
		if (m_log_console == true) {

		}
// ******************************************
		break;
	case MSG_MESSAGE:
		if(m_hwnd_monitor)
		{
			if(msg[strlen(msg)-1]=='\n')msg[strlen(msg)-1]='\0';
			SendMessage(GetDlgItem(m_hwnd_monitor,IDC_LST_MSG),LB_ADDSTRING,0,(LPARAM)msg);
			int count=SendMessage(GetDlgItem(m_hwnd_monitor,IDC_LST_MSG),LB_GETCOUNT,0,0);
			if(count>8 && autoScroll == true)
				SendMessage(GetDlgItem(m_hwnd_monitor,IDC_LST_MSG),LB_SETTOPINDEX,(WPARAM)count-8,0);
		}
		if (m_log_monitor == true) {
			if (msg[strlen(msg)-1] == '\0') {
				msg[strlen(msg)-1]='\n';
			} else if (msg[strlen(msg)] != '\n'){
				msg[strlen(msg)]='\n';
			}
			LogToFile("Monitor.log",false,msg);
		}
		break;
	case MSG_KEY:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_KEY,msg);
		}
		break;
	case MSG_ECM:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_ECM,msg);
		}
		break;
	case MSG_CA:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_CA,msg);
		}
		break;
	case MSG_ECM_DATA:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_ECM_DATA,msg);
		}
		break;
	case MSG_EMM:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_EMM,msg);
		}
		break;
	case MSG_EMM_DATA:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_EMM_DATA,msg);
		}
		break;
	case MSG_EMM_STATUS:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_EMM_STATUS,msg);
		}
		break;
	case MSG_PROCESS:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_PROCESS,msg);
		}
		break;
	case MSG_DATAFLOW:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_DATAFLOW,msg);
		}
		break;
	case MSG_PIDINFO:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_STC_PID,msg);
		}
		break;
	case MSG_PMT_DATAFLOW:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_PMT_DATAFLOW,msg);
		}
		break;
	case MSG_PMT_DATA:
		if(m_hwnd_monitor)
		{
			SetDlgItemText(m_hwnd_monitor,IDC_PMT_DATA,msg);
		}
		break;
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StartECMFilter(TProgramm82* CurrentProgramm)
{
	if (!m_plg_active || !m_ecm_active) return false;
#ifdef THREADED_STATE_CONTROL
	return SignalStartFilter(EVENT_STARTECMFILTER);
#else
	return StartECMFilter_TH(CurrentProgramm);
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StartECMFilter_TH(TProgramm82* CurrentProgramm)
{
	if (m_isecmfilter) return true;

	if(m_prevecmdata)
		delete [] m_prevecmdata;
	m_flow = 0;
	m_isecmfilter = false;
	m_prevecmdata = NULL;
	m_cursys = NULL;
	if(g_north_america)
		m_curecmindex = 0;

	if (!m_cursys) {
		do {
			delete m_cursys; m_cursys=0;
			m_cursys=cSystems::FindBySysId(CurrentProgramm->CA_System[m_curecmindex].CA_Typ,false,lastPri);
            if (m_cursys){
              int prio_type = CurrentProgramm->CA_System[m_curecmindex].EMM;
                  if ( ((strcmp(m_cursys->Name(),"Cardclient")==0) && (prio_type!=PPRIO_SOFTC )) ||
                  ((strcmp(m_cursys->Name(),"Cardclient")!=0)&& (prio_type!=PPRIO_CARDC )) )
               break;
                  else
                 lastPri=m_cursys->Pri();
            }
            else
            {
              m_curecmindex++;
              lastPri=0;
            }
         } while (m_curprg.CA_Anzahl > (m_curecmindex + g_north_america));
	}
	
	if (m_cursys) {
		lastPri=m_cursys->Pri();
		d(printf("\nNow try system %s with pri=%d\n",m_cursys->Name(),m_cursys->Pri()))
	//	Log("[INFO] System found: %s\n",m_cursys->Name());
	} else {
		PushMessage(MSG_CA,"CA:0x0000");
		printf("\nUnsupported CA Type.\n");
		m_curecmindex = 0;
		return false;
	}

	m_ecmfilter.Pid=CurrentProgramm->CA_System[m_curecmindex].ECM;

	if (m_cursys&&strcmp(m_cursys->Name(),"ConstCW") == 0) {
		cEcmInfo ecminfo(m_curprg.Name,m_curprg.CA_System[m_curecmindex].ECM,m_curprg.CA_System[m_curecmindex].CA_Typ,m_curprg.CA_System[m_curecmindex].Provider_Id);
		ecminfo.SetSource(m_curprg.SID_pid,0,m_curprg.freq/1000);
		if(m_cursys->ProcessECM(&ecminfo,NULL))
		{
			sprintf(m_keystr,"Key: %s",m_cursys->CurrentKeyStr());
			PushMessage(MSG_KEY,m_keystr);
			SendDCW(m_cursys->CW());
		}
		return true;
	}

	if (Start_Filter(&m_ecmfilter)) {
	//	d(printf("Sucessfully started ECM filter\n"))
		m_isecmfilter=true;
		m_logger.OnChannelChange(m_ecmfilter.Pid,m_emmfilter.Pid);
	} else {
		d(printf("Failed started filter\n"))
		return false;
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StopECMFilter()
{
#ifdef THREADED_STATE_CONTROL
	return SignalStopFilter(EVENT_STOPECMFILTER);
#else
	return StopECMFilter_TH();
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StopECMFilter_TH()
{
//	Log("Fnc :%s Line:%d\n",__FUNCTION__,__LINE__);
	if (m_isecmfilter) {
	//	return SignalStopFilter(EVENT_STOPECMFILTER);
	//	printf("\nStopECMFilter_TH\n");
		if (!Stop_Filter(m_ecmfilter.Running_ID)) {
			printf("!!BUG!!Host program cannot stop filter!\n");
		} else {
			m_isecmfilter=false;
			if (m_prevecmdata) {
				delete[] m_prevecmdata;
				m_prevecmdata=NULL;
			}
		//	PushMessage(MSG_ECM,"ECM:0x0000");
			PushMessage(MSG_DATAFLOW," ");
			PushMessage(MSG_ECM_DATA,"DATA: None");
		}
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StartEMMFilter(TProgramm82* CurrentProgramm)
{
	if (!m_plg_active || !m_emm_active) return false;
#ifdef THREADED_STATE_CONTROL
	return SignalStartFilter(EVENT_STARTEMMFILTER);
#else
	return StartEMMFilter_TH(CurrentProgramm);
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StartEMMFilter_TH(TProgramm82* CurrentProgramm)
{
	if (m_isemmfilter) return true;

	if (ProviderID == 0) {
		m_isemmfilter = false;
		StartECMFilter_TH(CurrentProgramm);
		return true;
	}

	char text[60];
	PushMessage(MSG_EMM,"EMM:0x0000");
	PushMessage(MSG_PROCESS,"-");
	PushMessage(MSG_EMM_STATUS,"<== Waiting for EMM Packet...");
	PushMessage(MSG_EMM_DATA,"DATA: None");

	m_cursys=cSystems::FindBySysId(CurrentProgramm->CA_System[m_curecmindex].CA_Typ,false,0);
	if (m_cursys && m_isemmfilter == false) {
		sprintf(text,"Provider: %s (0x%.4X)",m_cursys->Name(),ProviderID);
		PushMessage(MSG_PMT_DATA,text);
	//	d(printf("=> AU Provider: %s - Provider ID: (0x%.4X)\n",m_cursys->Name(),ProviderID))
	//	printf("Provider: %s 0x%.4X - EMM PID: 0x%.4X.\n", m_cursys->Name(), ProviderID, m_emmfilter.Pid); 

		m_emmcount = 0;
		EMMbufptr = 0;
		m_emmfilter.Filter_ID = EMM_FILTER_ID;
		if (Start_Filter(&m_emmfilter)) {
			d(printf("Sucessfully started EMM filter\n"))
			m_logger.OnChannelChange(m_ecmfilter.Pid,m_emmfilter.Pid);
			m_isemmfilter = true;
			m_emmcount=0;
		} else {
			d(printf("Failed to start EMM filter\n"))
			m_isemmfilter = false;
			return false;
		}
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StopEMMFilter()
{
#ifdef THREADED_STATE_CONTROL
	return SignalStopFilter(EVENT_STOPEMMFILTER);
#else
	return StopEMMFilter_TH();
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool DllClass::StopEMMFilter_TH()
{
//	Log("Fnc :%s Line:%d\n",__FUNCTION__,__LINE__);
	if (m_isemmfilter) {
	//	return SignalStopFilter(EVENT_STOPEMMFILTER);
	//	printf("\nStopEMMFilter_TH\n");
		if (!Stop_Filter(m_emmfilter.Running_ID)) {
			printf("!!BUG!!Host program cannot stop filter!\n");
		} else {
			m_isemmfilter = false;
			PushMessage(MSG_PROCESS," ");
			PushMessage(MSG_EMM_STATUS,"EMM-G filter: Stopped");
			PushMessage(MSG_EMM_DATA,"DATA: None");
		}
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StartDataFilter()
{
#ifdef THREADED_STATE_CONTROL
	return SignalStartFilter(EVENT_STARTPMTFILTER);
#else
	return StartDataFilter_TH();
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::UpdatePIDInfo(int FilterID)
{
	if(!m_hwnd_monitor) return;
	char txt[20];
	switch(FilterID)
	{
	case DATA_FILTER_ID:
		sprintf(txt,"PMT:0x%.4X(%d)",m_curprg.PMT_pid,m_curprg.PMT_pid);
		PushMessage(MSG_PIDINFO,txt);
		break;
	case ECM_FILTER_ID:
		sprintf(txt,"ECM:0x%.4X(%d)",m_curprg.CA_System[m_curecmindex].ECM,m_curprg.CA_System[m_curecmindex].ECM);
		PushMessage(MSG_ECM,txt);
		sprintf(txt,"CA:0x%04X",m_curprg.CA_System[m_curecmindex].CA_Typ);
		PushMessage(MSG_CA,txt);
		break;
	case EMM_FILTER_ID:
		sprintf(txt,"EMM:0x%.4X(%d)",m_curprg.CA_System[0].EMM,m_curprg.CA_System[0].EMM);
		PushMessage(MSG_EMM,txt);
		break;
	};
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::InitializeMenu()
{
	HMENU EmuMenu;
	if(m_DLLInstance.DVBCoreFlag)
	{
		EmuMenu = GetMenu(m_hwnd_monitor);
	}
	else
	{
		EmuMenu = GetMenu(m_MultiDecWindow);
	}

	CheckMenuItem(EmuMenu,IDM_ACTIVE,m_plg_active? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_BEEP_ON_KEY,m_beep_on_key? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_NORTH_AMERICA,m_north_america? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_ECM_ACTIVE,m_ecm_active? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_EMM_ACTIVE,m_emm_active? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_HIDE_BAD_MESSAGE,m_hide_bad_data? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_KEEP_KEYS,m_keep_old_keys? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOAD_MONITOR,m_load_monitor? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_PROCESS_EMM_S,m_process_emm_s? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_SHOW_CONSOLE,m_have_console? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_USE_MAX_PARSER,m_use_max_parser? MF_CHECKED:MF_UNCHECKED);

	CheckMenuItem(EmuMenu,IDM_LOGECMTEXT,m_logger.GetActive(cLogger::ECM_Text)? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOGECMBIN,m_logger.GetActive(cLogger::ECM_Bin)? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOGEMMTEXT,m_logger.GetActive(cLogger::EMM_Text)? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOGEMMBIN,m_logger.GetActive(cLogger::EMM_Bin)? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOGCW,m_logger.GetActive(cLogger::DCW)? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOG_EMM_G,m_log_emm_g? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOG_MONITOR,m_log_monitor? MF_CHECKED:MF_UNCHECKED);
	CheckMenuItem(EmuMenu,IDM_LOG_CONSOLE,m_log_console? MF_CHECKED:MF_UNCHECKED);

    switch (m_show_diss) {
		case 0: 
			CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_BASIC,MF_CHECKED);
			break;
		case 1: 
			CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_DEBUG,MF_CHECKED);
			break;
		case 2: 
			CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_VERBOSE,MF_CHECKED);
			break;
		default:
			m_show_diss = 0;
			CheckMenuItem(EmuMenu,IDM_SHOW_DISASSEMBLY_BASIC,MF_CHECKED);
			break;
	}

    switch(m_log_emm_by) {
		case 0: 
			CheckMenuItem(EmuMenu,IDM_LOGEMM_OFF,MF_CHECKED);
			break;
		case 1: 
			CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYSINGLEFILE,MF_CHECKED);
			break;
		case 2: 
			CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYTWOFILES,MF_CHECKED);
			break;
		case 3: 
		default:
			CheckMenuItem(EmuMenu,IDM_LOGEMM_LOGBYCAMID,MF_CHECKED);
			break;
	}
	CheckMenuItem(EmuMenu,IDM_LOGCAMID,m_log_camid? MF_CHECKED:MF_UNCHECKED);

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::On_Channel_Change_TH( const struct TProgramm82 &CurrentProgramm )
{
	char text[60];

	StopECMFilter();
	StopEMMFilter();
	StopDataFilter();
	m_pmtok=false;
	StartEMMrx = false;
	doOnce = true;

//	m_clearfilter_event = true;
	ClearAllData();

	//Menu Initialization
	InitializeMenu();

	m_curecmindex=0;
	lastPri=0;
	PushMessage(MSG_PIDINFO,"PMT:0x0000");
	PushMessage(MSG_PMT_DATAFLOW," ");
	PushMessage(MSG_PMT_DATA," ");
	PushMessage(MSG_ECM,"ECM:0x0000");
	PushMessage(MSG_DATAFLOW," ");
	PushMessage(MSG_CA,"CA:0x0000");
	PushMessage(MSG_ECM_DATA,"DATA: None");
	PushMessage(MSG_KEY,"Key: None");
	PushMessage(MSG_EMM,"EMM:0x0000");
	PushMessage(MSG_PROCESS," ");
	PushMessage(MSG_EMM_STATUS,"EMM-G filter: OFF");
	PushMessage(MSG_EMM_DATA,"DATA: None");

	sprintf(text,"Ch: %d - %.45s ",CurrentProgramm.SID_pid, CurrentProgramm.Name);
	PushMessage(MSG_INFO,text);

	m_logger.OnChannelChange((char*)CurrentProgramm.Name,CurrentProgramm.ECM_PID,m_emmfilter.Pid);

//	Log("Fnc :%s Line:%d\n",__FUNCTION__,__LINE__);
	if(m_plg_active) {
		StartDataFilter();
		Sleep(1000);

		if(g_north_america)
		{
			//Start ECM Processing ?
				if (m_ecm_active == true  &&  m_isecmfilter == false) {
					if(!StartECMFilter((TProgramm82*)&CurrentProgramm)) {
						printf("[ERROR] Cannot start ECM pid filter!\n");
					} else {
					}
				}

			//Start EMM Processing ?
				if (m_emm_active == true  &&  m_isemmfilter == false) {
					if(!StartEMMFilter((TProgramm82*)&CurrentProgramm)) {
						printf("[ERROR] Cannot start EMM pid filter!\n");
					} else {
					}
				}
		}
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void MAIN_CTRL(void* impl)
{
	DllClass* imp=(DllClass*)impl;
	imp->On_Channel_Change_TH(imp->m_curprg);
	imp->Log("OnChannelChange Thread\n");
	while (!imp->IsChChanged) Sleep(50);
	imp->StopEMMFilter();
	imp->StopECMFilter();
	imp->StopDataFilter();
	imp->m_pmtok=false;
	// m_clearfilter_event = true;
	imp->ClearAllData();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::On_Channel_Change( const struct TProgramm82 &CurrentProgramm )
{
	if(!DVBCoreFlag)
	{
		CAPI_Impl::On_Channel_Change(CurrentProgramm);
		Get_Programm_Detail(&m_curprg);
	}
#ifdef MAIN_LOOP_THREADED
	IsChChanged = true;
	Sleep(100);
	IsChChanged = false;
	_beginthread( MAIN_CTRL, 0, this );
#else
	On_Channel_Change_TH( CurrentProgramm );
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::FoundKey()
{
	TProgramm82 curprg;
	Log("[INFO] Found Keys!\n");
	StopEMMFilter();
	ClearAllData();
	if(!DVBCoreFlag)
		Get_Programm_Detail(&curprg);
    m_curecmindex=0;

	StartECMFilter(&curprg);

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
char* TrimSpaces(char* line,char* trimmed)
{
	int tind=0;

	char* len =line +strlen(line);

	memset(trimmed,0,sizeof(trimmed));
	while(line!=len)
	{
		if(*line==';'||*line=='\0')break;
		trimmed[tind]=*line;
		tind++;line++;
		if(isspace(*line)){
			line=skipspace(line);
			trimmed[tind]=' ';
			tind++;
		}
	}
	if(trimmed[tind-1]==' ')tind--;
	trimmed[tind]='\0';
	return trimmed;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
char* DllClass::RemoveComment(char* line)
{
	int len  = strlen(line);
	char* data = &line[0];

	//Look for comment
	for (int it=0; it<len; it++) {
		if (data[it] == ';' || line[it] == '\0') {
			data[it] = '\0';
			len=it;
		}
	}

	//Remove trailing spaces and tabs
	while(len-- != 0)
	{
		if (data[len] == 0x20 || data[len] == 0x9)  {
			data[len] = '\0';
		} else {
			len = 0;
		}
	}
	return data;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::SaveKey(char type,int keyid,int keynr,unsigned char* key,int keylen)
{
	if (m_use_ca_cache) return;
    char keyfilestr[_MAX_PATH];
    char tempfilestr[_MAX_PATH];
	unsigned char* oldkey;

	if (strlen(SCpath)) {
		sprintf(keyfilestr,"%s\\%s",SCpath,"Softcam.key");
	} else {
		sprintf(keyfilestr,"%s\\%s",dlldirPath,"Softcam.key");
	}
    sprintf(tempfilestr,"%s\\%s",dlldirPath,"~tempkeys");

//    m_DLLInstance.Log("%s\n",keyfilestr);
//    m_DLLInstance.Log("%s\n",tempfilestr);

	FILE* keyfile=fopen(keyfilestr,"r");
	FILE* ft=fopen(tempfilestr,"w");
	bool updated=false;
	char out_buffer[512];
	char oldkeystr[128];
	char newkeystr[128];
	char skeystr[128];
	char date_str[30];
	char time_str[20];
	char cur_key[300];
	const char* _upt_str="%s ; Updated by EmuNation At: %s On: %s\n";
	char _key_str[100];

	if (type == 'N') {
		strcpy(_key_str,"%c %.4X %.2X %s");
	} else if (type == 'V') {
		strcpy(_key_str,"%c %.6X %.2X %s");
	}

	_strdate(date_str);
	_strtime(time_str);
	HexStr(skeystr,key,16);
	sprintf(newkeystr,_key_str,type,keyid,keynr,skeystr);
	sprintf(oldkeystr, "%.9s", newkeystr);
//	d(printf("oldkeystr: %s - newkeystr: %s\n",oldkeystr,newkeystr));
//	d(printf("New Key: %s\n",newkeystr));
	sprintf(out_buffer,"[%s] New Key: %s\n",time_str,newkeystr);
	PushMessage(MSG_MESSAGE,out_buffer);

		if(keyfile) {
			char* line;
			char buff[300];
			memset(buff,0,sizeof(buff));
			while(fgets(buff,sizeof(buff),keyfile)) {
				line=skipspace(stripspace(buff));
				if(updated){fprintf(ft,"%s\n",line);continue;}
				if(line[0]==';'||line[0]=='#') {
					fprintf(ft,"%s\n",line);
					continue;
				}
				TrimSpaces(line,cur_key);

				if(strncmp(cur_key,oldkeystr,strlen(oldkeystr))==0) {
					if (m_keep_old_keys) {
						fprintf(ft,";%s\n",line);
					}
					fprintf(ft,_upt_str, newkeystr, time_str, date_str);
					updated=true;
				} else {
					fprintf(ft,"%s\n",line);
				}

				memset(buff,0,sizeof(buff));
			}
		}
		if(!updated)fprintf(ft,_upt_str,newkeystr,date_str);
		fclose(keyfile);
		fclose(ft);
		remove(keyfilestr);
		rename(tempfilestr,keyfilestr);
		if (!m_isecmfilter) {
//			d(printf(" m_isecmfilter = false\n"));
		}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Public member variables
//------------------------------------------------------------------------------
void DllClass::CreateDir(char* Path)
{
 char DirName[256];
 char* p = Path;
 char* q = DirName; 
 while(*p)
 {
   if (('\\' == *p) || ('/' == *p))
   {
     if (':' != *(p-1))
     {
        CreateDirectory(DirName, NULL);
     }
   }
   *q++ = *p++;
   *q = '\0';
 }
 CreateDirectory(DirName, NULL);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::logCamID(char* filename, unsigned char* data, int length, bool binary, char* text)
{
//	if (!m_log_camid) return;
	char time_str[20];
	char date_str[30];
	char LogPath[_MAX_PATH];
	_strdate(date_str);
	_strtime(time_str);

	sprintf(LogPath,"%s\\%s",GetLogPath(),filename);
	FILE* f= fopen (LogPath,binary?"atb":"at");
    if (binary)
    {
        fwrite(data,length,1,f);
	} else {
        char* txtdata= new char [length*2+1];
        HexStr(txtdata,data,length);
        fprintf(f,"[%s-%s] %s %s\n",date_str,time_str,text,txtdata);
        delete[] txtdata;
    }
    if(f)
        fclose(f);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::to_binlog(char* filename,unsigned char* data,int length)
{
	char LogPath[_MAX_PATH];
	sprintf(LogPath,"%s\\%s",GetLogPath(),filename);
	FILE* f=fopen(LogPath,"atb");
	if(f)
	{
		fwrite(data,length,1,f);
		fclose(f);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::InitializeOptions()
{
	char *tempHex = new char[4];
	memset(tempHex, 0x00, 4);

	//EmuNation Section
	m_use_aux_emu=GetPrivateProfileInt("EmuNation", "AuxEmu", 0, INI_FILE);
	::NADCWTweak=GetPrivateProfileInt("EmuNation", "NADCWTweak", 0, INI_FILE);
	memset(SCpath, 0x00, 255);
	GetPrivateProfileString("EmuNation", "S_K_location", "", SCpath, 255, INI_FILE);
	RemoveComment(SCpath);
	m_use_ca_cache=GetPrivateProfileInt("EmuNation", "UseCaCache", 0, INI_FILE);
	m_north_america=GetPrivateProfileInt("EmuNation", "NorthAmerica", 1, INI_FILE);
	g_north_america = m_north_america;

	//Menu Options Section
	m_plg_active=GetPrivateProfileInt("Menu Options", "Active", 1, INI_FILE);
	m_beep_on_key=GetPrivateProfileInt("Menu Options", "BeepOnKeyFind", 1, INI_FILE);
	g_BeepOnNewKey = m_beep_on_key;
	m_hide_bad_data=GetPrivateProfileInt("Menu Options", "HideBadData", 1, INI_FILE);
	m_keep_old_keys=GetPrivateProfileInt("Menu Options", "KeepOldKeys", 0, INI_FILE);
	m_load_monitor=GetPrivateProfileInt("Menu Options", "LoadMonitorOnStart", 0, INI_FILE);
	m_ecm_active=GetPrivateProfileInt("Menu Options", "ProcessECM", 1, INI_FILE);
	m_emm_active=GetPrivateProfileInt("Menu Options", "ProcessEMM", 1, INI_FILE);
	m_process_emm_s=GetPrivateProfileInt("Menu Options", "ProcessEmmS", 0, INI_FILE);
	m_have_console=GetPrivateProfileInt("Menu Options", "ShowConsole", 0, INI_FILE);
	m_show_diss=GetPrivateProfileInt("Menu Options", "ShowDisassembly", 0, INI_FILE);
	m_use_max_parser=GetPrivateProfileInt("Menu Options", "UseMaxParser", 1, INI_FILE);

	//Logger Section
	m_loge3=GetPrivateProfileInt("Logger", "LogE3", 0, INI_FILE);
	g_LogE3 = m_loge3;
	m_log_camid=GetPrivateProfileInt("Logger", "LogRawEMM-S", 0, INI_FILE);
	m_log_emm_by=GetPrivateProfileInt("Logger", "LogEMM-S", 0, INI_FILE);
	m_log_emm_g=GetPrivateProfileInt("Logger", "LogEMM-G", 0, INI_FILE);
	m_log_monitor=GetPrivateProfileInt("Logger", "LogMonitor", 0, INI_FILE);
	m_log_console=GetPrivateProfileInt("Logger", "LogConsole", 0, INI_FILE);
	m_logger.SetActive(GetPrivateProfileInt("Logger", "LogDCW", 0, INI_FILE), cLogger::DCW);
	m_logger.SetActive(GetPrivateProfileInt("Logger", "LogECMBin", 0, INI_FILE), cLogger::ECM_Bin);
	m_logger.SetActive(GetPrivateProfileInt("Logger", "LogECMText", 0, INI_FILE), cLogger::ECM_Text);
	m_logger.SetActive(GetPrivateProfileInt("Logger", "LogEMMBin", 0, INI_FILE), cLogger::EMM_Bin);
	m_logger.SetActive(GetPrivateProfileInt("Logger", "LogEMMText", 0, INI_FILE), cLogger::EMM_Text);
	memset(strLogPath, 0x00, 255);
	GetPrivateProfileString("Logger", "LogPath", "", strLogPath, 255, INI_FILE);
	RemoveComment(strLogPath);

	//AuxServer Section
	::auxAddr = new char[255];
	memset(::auxAddr, 0x00, 255);
	GetPrivateProfileString("AuxServer", "Address", "auxserver", ::auxAddr, 255, INI_FILE);
	::auxPass = new char[255];
	memset(::auxPass, 0x00, 255);
	GetPrivateProfileString("AuxServer", "Password", "auxserver", ::auxPass, 255, INI_FILE);
	::auxPort = GetPrivateProfileInt("AuxServer", "Port", 7777, INI_FILE);

	//Provider 0101 Section
	GetPrivateProfileString("0101", "emm_pid_hex", "0120", tempHex, 5, INI_FILE);
	sscanf(tempHex, "%4x", &emm_0101_pid_hex);
	m_Start_0101_EmmOnChange=GetPrivateProfileInt("0101", "EMMOnChannelChange", 1, INI_FILE);
	m_Stop_0101_EmmOnChange=GetPrivateProfileInt("0101", "EMM_off_OnKeyFind", 0, INI_FILE);

	//Provider 0901 Section
	GetPrivateProfileString("0901", "emm_pid_hex", "00F9", tempHex, 5, INI_FILE);
	sscanf(tempHex, "%4x", &emm_0901_pid_hex);
	m_Start_0901_EmmOnChange=GetPrivateProfileInt("0901", "EMMOnChannelChange", 1, INI_FILE);
	m_Stop_0901_EmmOnChange=GetPrivateProfileInt("0101", "EMM_off_OnKeyFind", 0, INI_FILE);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::SaveOptions()
{
	//EmuNation Section
	WritePrivateProfileInt("EmuNation","NADCWTweak",::NADCWTweak,INI_FILE);
	//Logger Section
	WritePrivateProfileInt("Logger","LogDCW",m_logger.GetActive(cLogger::DCW),INI_FILE);
	WritePrivateProfileInt("Logger","LogECMBin",m_logger.GetActive(cLogger::ECM_Bin),INI_FILE);
	WritePrivateProfileInt("Logger","LogEMMBin",m_logger.GetActive(cLogger::EMM_Bin),INI_FILE);
	WritePrivateProfileInt("Logger","LogECMText",m_logger.GetActive(cLogger::ECM_Text),INI_FILE);
	WritePrivateProfileInt("Logger","LogEMMText",m_logger.GetActive(cLogger::EMM_Text),INI_FILE);
	//AuxServer Section
	WritePrivateProfileString("AuxServer","Address",::auxAddr,INI_FILE);
	WritePrivateProfileString("AuxServer","Password",::auxPass,INI_FILE);
	WritePrivateProfileInt("AuxServer","Port",::auxPort,INI_FILE);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
unsigned char SAA_COMMAND_DW1[16]={0x07,0x00,0x10,0x04,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
unsigned char SAA_COMMAND_DW2[16]={0x07,0x00,0x10,0x04,0x05,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
#define SIZE_HOLD_DATA 26
bool DllClass::IsCWNecessary(unsigned char* _Data)
{
	if(m_prevecmdata)
	{
		if(memcmp(m_prevecmdata,_Data,SIZE_HOLD_DATA)==0) return false;
		else
		{
			delete [] m_prevecmdata;
			m_prevecmdata=new unsigned char[SIZE_HOLD_DATA];
			memcpy(m_prevecmdata,_Data,SIZE_HOLD_DATA);
			return true;
		}
	}
	else
	{
		m_prevecmdata=new unsigned char[SIZE_HOLD_DATA];
		memcpy(m_prevecmdata,_Data,SIZE_HOLD_DATA);
		return true;
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::WriteECM(unsigned char* _Data)
{
	if(m_hwnd_monitor)
	{
		HexStrSpaced(m_ecmtext,_Data,SCT_LEN((&_Data[1]))&0xBC );
		PushMessage(MSG_ECM_DATA,m_ecmtext);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::WriteEMM(unsigned char* _Data)
{
	if(m_hwnd_monitor)
	{
		HexStrSpaced(m_emmtext,_Data,SCT_LEN((&_Data[1]))&0xBC );
		PushMessage(MSG_EMM_DATA,m_emmtext);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::WriteCamIDinfo(unsigned char* _Data, int len, bool Raw)
{
//	HexStrSpaced(m_camtext,_Data,SCT_LEN((&_Data[1]))&0xBC );
//	HexStrSpaced(m_camtext,_Data,SCT_LEN(_Data) );
	HexStrSpaced(m_camtext,_Data,len);
	if (Raw) {
		PushMessage(MSG_CAMID_RAW,m_camtext);
	} else {
		PushMessage(MSG_CAMID_DECRYPT,m_camtext);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::Turn(int count)
{
	char str[20];
	const char turn[4]={'-','\\','|','/'};

	sprintf(str,"%c",turn[count%4]);
	PushMessage(MSG_PROCESS,str);

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void STATE_CONTROL(void* impl)
{
	DllClass* imp=(DllClass*)impl;

	while(true)
	{
		if(imp->m_FilterEvents.empty())
		{
			Sleep (10);
			continue;
		}
		if(imp->m_clearfilter_event)
		{
			while(!imp->m_FilterEvents.empty()) imp->m_FilterEvents.pop();
			imp->m_clearfilter_event = false;
			continue;
		}
		imp->PluginEvent = imp->m_FilterEvents.front();
		imp->m_FilterEvents.pop();

		switch(imp->PluginEvent)
		{
		case EVENT_STOPPMTFILTER:
			imp->StopDataFilter_TH();
			break;
		case EVENT_STOPEMMFILTER:
			imp->StopEMMFilter_TH();
			break;
		case EVENT_STOPECMFILTER:
			imp->StopECMFilter_TH();
			break;
			//Start events
		case EVENT_STARTPMTFILTER:
			imp->StartDataFilter_TH();
			Sleep(100);
			break;
		case EVENT_STARTEMMFILTER:
			imp->StartEMMFilter_TH(&(imp->m_curprg));

			break;
		case EVENT_STARTECMFILTER:
			imp->StartECMFilter_TH(&(imp->m_curprg));
			Sleep(100);
			break;
		}
		imp->PluginEvent=EVENT_IDLE;
		Sleep(10);
	}
	_endthread();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::StopDataFilter()
{
#ifdef THREADED_STATE_CONTROL
	return SignalStopFilter(EVENT_STOPPMTFILTER);
#else
	return StopDataFilter_TH();
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::SignalStartFilter (int type)
{
	//PluginEvent=type;
	//while(PluginEvent!=EVENT_IDLE)Sleep(1);
	m_FilterEvents.push(type);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::SignalStopFilter (int type)
{
	//PluginEvent=type;
	//while(PluginEvent!=EVENT_IDLE)Sleep(1);
	m_FilterEvents.push(type);
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::ProcessPMT(unsigned char* _Data,int DataLen)
{
#ifdef PMT_PROCESSING
	m_dvbp.SetBuffer(&_Data[1],DataLen,cDVBParser::DVB_PACKETTYPE_MDAPI);

	PushMessage(MSG_PMT_DATAFLOW,(char*)(m_PMTflow%2?"*":" "));
	m_PMTflow++; m_PMTflow %= 2;

	StopDataFilter();
	if(m_dvbp.AnalyzePacket(cDVBParser::DVB_PACKETMODE_SECTION))
	{
		cPMT* pmt=(cPMT*)m_dvbp.GetPacketData();
		if(pmt->program_number!=m_curprg.SID_pid)return false;
		sprintf(m_keystr,"Ch: %d - %.45s\n",m_curprg.SID_pid,m_curprg.Name);
		PushMessage(MSG_INFO,m_keystr);
		//printf("\n%s",m_keystr);
		
		StopDataFilter();
		ClearAllData();
		//SignalStopFilter(EVENT_STOPPMTFILTER);
		cMPEGCADescriptor* data;
	//	printf("-------------CA TABLE-------------\n");
		//if(m_curprg.CA_Anzahl==0) return true;
		for(int i=0;i<pmt->Count();i++)
		{
			data=(cMPEGCADescriptor*)pmt->Get(i);
   //		printf("| System:%.4X PID:%.4X PRV:%.6X\n",data->CA_system_ID,data->CA_PID,data->prov_id);
			m_curprg.CA_System[i].ECM=data->CA_PID;
			m_curprg.CA_System[i].CA_Typ=data->CA_system_ID;
			m_curprg.CA_System[i].Provider_Id=data->prov_id;

			m_curprg.CA_System[i].EMM=PPRIO_NO;
		}
   //   printf("----------------------------------\n");

        m_curprg.CA_Anzahl=m_provs.Prioritize(&m_curprg,pmt->Count());
//      m_curprg.CA_Anzahl=pmt->Count()
		if(!g_north_america)
		{
			if (m_ecm_active == true &&  m_isecmfilter == false && m_curprg.CA_Anzahl>0) {
			   if(!StartECMFilter(&m_curprg)) {
				  printf("[ERROR] Cannot start ECM pid filter!\n");
			   }
			} 
		}
		return true;
	}
	return false;
#endif
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
unsigned char cw_p[16]={0};

void DllClass::SendDCW(unsigned char* cw)
{
	//Fix Checksum
	cw[3]=cw[0]+cw[1]+cw[2];
	cw[7]=cw[4]+cw[5]+cw[6];
	cw[11]=cw[8]+cw[9]+cw[10];
	cw[15]=cw[12]+cw[13]+cw[14];

	for(int i=0,j=1;i<8;i+=2,j+=2)
	{
		SAA_COMMAND_DW1[j+8]=cw[i];
		SAA_COMMAND_DW2[j+8]=cw[i+8];

		SAA_COMMAND_DW1[j-1+8]=cw[i+1];
		SAA_COMMAND_DW2[j-1+8]=cw[i+9];

	}

	TDVB_COMMAND dw1,dw2;
	memcpy(&dw1,SAA_COMMAND_DW1,sizeof(SAA_COMMAND_DW1));
	memcpy(&dw2,SAA_COMMAND_DW2,sizeof(SAA_COMMAND_DW2));
	//SendMessage(m_MultiDecWindow,WM_USER,MDAPI_DVB_COMMAND,(LONG)&SAA_COMMAND_DW1);
	//SendMessage(m_MultiDecWindow,WM_USER,MDAPI_DVB_COMMAND,(LONG)&SAA_COMMAND_DW2);
	m_logger.LogData(cw,16,cLogger::DCW);


	if(memcmp(cw_p+8,cw+8,8)!=0)
	{
		//printf("DCW2.");
		if(!DVBCoreFlag)
		{
			SendMDCommand(dw2);
			SendTTCommand(dw2);
		}
		else
			_DVBSendSAACmd(dw2.Cmd_Buffer,dw2.Cmd_laenge);
		if(::NADCWTweak) Sleep(75); //NADCWTweak: 75ms between DCWs prevents 2nd ECM freeze in NA after channel change
	}

	if(memcmp(cw_p,cw,8)!=0)
	{
		//printf("DCW1.");
		if(!DVBCoreFlag)
		{
			SendMDCommand(dw1);
			SendTTCommand(dw1);
		}
		else
			_DVBSendSAACmd(dw1.Cmd_Buffer,dw1.Cmd_laenge);
		if(::NADCWTweak) Sleep(75); //NADCWTweak: 75ms between DCWs prevents 2nd ECM freeze in NA after channel change
	}

    if(::NADCWTweak) //NADCWTweak: Send extra DCW (2,1,2) on channel change to prevent initial freeze
    {
		if((memcmp(cw_p,cw,8)!=0) && (memcmp(cw_p+8,cw+8,8)!=0))
		{
			//printf("DCW2.");
			if(!DVBCoreFlag)
			{
				SendMDCommand(dw2);
				SendTTCommand(dw2);
			}
			else
				_DVBSendSAACmd(dw2.Cmd_Buffer,dw2.Cmd_laenge);
			if(::NADCWTweak) Sleep(75); //NADCWTweak: 75ms between DCWs prevents 2nd ECM freeze in NA after channel change
		}
	}

	memcpy(cw_p,cw,16);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::GetState()
{
	return ThreadState;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
LRESULT DllClass::APIWndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
	return CAPI_Impl::APIWndProc(msg,wparam,lparam);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DATA_FLOW(void* impl)
{

	DllClass* imp=(DllClass*)impl;
	FILTERDATA d,d1,d2;
	// imp->LOG_DEBUG_PLG("Start")
	while(imp->GetState())
	{
		if(imp->m_clearevent)
		{
			while(!imp->m_PMTq.empty())imp->m_PMTq.pop();
			while(!imp->m_ECMq.empty())imp->m_ECMq.pop();
			while(!imp->m_EMMq.empty())imp->m_EMMq.pop();
			imp->m_clearevent=false;
			continue;
		}
		if(!imp->m_PMTq.empty())
		{

			d=imp->m_PMTq.front();
			imp->ProcessPMTData(&d);
			imp->m_PMTq.pop();
			imp->UpdatePIDInfo(DATA_FILTER_ID);

		}
		if(!imp->m_EMMq.empty())
		{
			d1=imp->m_EMMq.front();
			imp->ProcessEMMData(&d1);
			imp->m_EMMq.pop();
			imp->UpdatePIDInfo(EMM_FILTER_ID);
		}
		if(!imp->m_ECMq.empty())
		{
			d2=imp->m_ECMq.front();
			imp->ProcessECMData(&d2);
			imp->m_ECMq.pop();
			imp->UpdatePIDInfo(ECM_FILTER_ID);
		}

		Sleep(10);
	}
	// imp->LOG_DEBUG_PLG("END")
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::ClearAllData()
{
	m_clearevent=true;
	Sleep(50);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::ProcessPMTData(FILTERDATA* fltdata)
{
	unsigned char* _Data=fltdata->Data;

	if(!m_pmtok)
	{
		if(ProcessPMT(_Data,myDataLen))m_pmtok=true;
	}

}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
bool DllClass::SetProvider()
{
	char text[60];

  // dish
	if ((ProviderID == 0x0001) ||
	    (ProviderID >= 0x0101 && ProviderID <= 0x010F)) {
		StartEMMrx = m_StartEmm_OnChange = m_Start_0101_EmmOnChange;
		m_curprg.CA_System[m_curecmindex].Provider_Id = ProviderID; 
		m_curprg.CA_System[m_curecmindex].EMM = emm_0101_pid_hex; 
		m_emmfilter.Pid = emm_0101_pid_hex;
		m_StopEmm_OnChange = m_Stop_0101_EmmOnChange;
  // bev
	} else if ((ProviderID == 0x0801) ||
			   (ProviderID >= 0x0901 && ProviderID <= 0x090F)) {
		StartEMMrx = m_StartEmm_OnChange = m_Start_0901_EmmOnChange;
		m_curprg.CA_System[m_curecmindex].Provider_Id = ProviderID; 
		m_curprg.CA_System[m_curecmindex].EMM = emm_0901_pid_hex; 
		m_emmfilter.Pid = emm_0901_pid_hex;
		m_StopEmm_OnChange = m_Stop_0901_EmmOnChange;
  // microspace
	} else if (ProviderID == 0x2101) {
		StartEMMrx = m_StartEmm_OnChange = false;
		m_StopEmm_OnChange = false;
  // others
	} else {
		StartEMMrx = m_StartEmm_OnChange = false;
		m_StopEmm_OnChange = true;
	}
	sprintf(text,"Provider: %s (0x%.4X)",m_cursys->Name(),ProviderID);
	PushMessage(MSG_PMT_DATA,text);
	PushMessage(MSG_PMT_DATAFLOW," ");
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::ProcessECMData(FILTERDATA* fltdata)
{
	unsigned char* _Data=fltdata->Data;
//	int DataLen=184;

	cEcmInfo ecminfo(m_curprg.Name,m_curprg.CA_System[m_curecmindex].ECM,m_curprg.CA_System[m_curecmindex].CA_Typ,m_curprg.CA_System[m_curecmindex].Provider_Id);
	ecminfo.SetSource(m_curprg.SID_pid,0,m_curprg.freq/1000);
	PushMessage(MSG_DATAFLOW,(char*)(m_flow%2?"*":" "));
	m_flow++; m_flow %= 2;

if (g_north_america)
{
//******************************************************** (01/17/2007) **
	if (doOnce == true) {
		unsigned char *data = &_Data[1];
		ProviderID = (data[5]*256) + data[6];
		doOnce = !SetProvider();
		if (!m_ecm_active) {
			PushMessage(MSG_DATAFLOW,(char*)" ");
			StopECMFilter();
			ClearAllData();
		}
		if (m_emm_active == true  &&  m_isemmfilter == false) {
			StartEMMFilter(&m_curprg);
		}
	}
//******************************************************** (01/17/2007) **
}
	if (!m_plg_active || !m_ecm_active) return;

	if (m_cursys) {
		if (!IsCWNecessary(_Data)) return;

		if (m_cursys->ProcessECM(&ecminfo,&_Data[1])) {
			SendDCW(m_cursys->CW());

			m_logger.LogData(_Data,myDataLen,cLogger::ECM_Text);
			m_logger.LogData(_Data,myDataLen,cLogger::ECM_Bin);

			WriteECM(_Data);
			sprintf(m_keystr,"Key: %s",m_cursys->CurrentKeyStr());
			PushMessage(MSG_KEY,m_keystr);

			return;
		} else {
			//sprintf(m_keystr,"Key: %c %.2x %.2x",m_cursys->badType(),m_cursys->badId(),m_cursys->badKeynr());
			sprintf(m_keystr,"Key: %c %04X %.2X",m_cursys->badType(),ProviderID,m_cursys->badKeynr());
			PushMessage(MSG_KEY,m_keystr);

#if 0
			//If cannot decode
			if (strcmp("Nagra",m_cursys->Name())== 0 ||
			    strcmp("Nagra2",m_cursys->Name())== 0
				//				 ||strcmp("Viaccess",m_cursys->Name())==0
				//				 ||strcmp("Seca",m_cursys->Name())==0
				)
			{

				//If nagra need auto update
//				StopECMFilter();
//				ClearAllData();
				//SignalStopFilter(EVENT_STOPECMFILTER);

//				if (!m_StartEmm_OnChange) {
//					StartEMMFilter(&m_curprg);
//				}
                
//				strcpy(m_ecmtext,"No data present for saving CPU time!");
//				PushMessage(MSG_ECM,m_ecmtext);
				return;
			}
#endif
		}
	}

	if(!g_north_america)
	{
		StopECMFilter();
		ClearAllData();
		SignalStopFilter(EVENT_STOPECMFILTER);
		Sleep(50);
		StartECMFilter(&m_curprg);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::ProcessEMMData(FILTERDATA* fltdata)
{
	if (!m_plg_active || !m_emm_active) return;
	unsigned char* _Data=fltdata->Data;
	unsigned short startPoint = 0;
	char text[30];
    char filename[_MAX_PATH];
	char EMM_filename[_MAX_PATH];
	int LRC = 0x41;

	if ( m_cursys && ProviderID > 0 )
	{
	//CamID Processor
		if (_Data[1] == 0x83) {
			sprintf(CamIDnumber,"%02X%02X%02X%02X",_Data[6],_Data[5],_Data[4],_Data[7]);
		}
		if (_Data[1] == 0x83 && m_log_emm_by > 0 && m_log_camid == true){
//			int LogType = m_log_emm_by;
			switch(m_log_emm_by) {
				case 0:			//0=no logging
					sprintf(EMM_filename,"");
					break;
				case 1:			//1=single file
					sprintf(EMM_filename,"EMM-S.log");
					break;
				case 2:			//2= two files (group / cam spec.)
					if (_Data[7] == 0x00) {
						sprintf(EMM_filename,"EMM-S_By_Group.log");
					} else {
						sprintf(EMM_filename,"EMM-S_By_Target.log");
					}
					break;
				case 3:			//3=by camid
				default:
					sprintf(EMM_filename,"CamID_%02X%02X%02X%02X.log",_Data[6],_Data[5],_Data[4],_Data[7]);
					break;
			}
			sprintf(text,"Raw Packet: ");
			logCamID(EMM_filename,_Data,myDataLen,false,text);
		} 

		cEcmInfo emminfo(m_curprg.Name,m_curprg.CA_System[0].EMM,m_curprg.CA_System[0].CA_Typ,m_curprg.CA_System[0].Provider_Id);

		m_logger.LogData(_Data,myDataLen,cLogger::EMM_Bin);
		if ((ProviderID >= 0x0101 && ProviderID <= 0x010F) ||
			 ProviderID == 0x0001 || ProviderID == 0x0801  || 
			(ProviderID >= 0x0901 && ProviderID <= 0x090F)) {
			printf("*");
			m_cursys->ProcessEMM(m_curprg.CA_System[m_curecmindex].EMM,emminfo.provId,&_Data[_Data[0]+1]);
			m_logger.LogData(_Data,myDataLen,cLogger::EMM_Text);
			m_logger.LogData(_Data,myDataLen,cLogger::EMM_Bin);
			if(m_isecmfilter){
				emmTimeout = 100;
			} else {
				emmTimeout = 1000;
			}
			++m_emmcount;
		}
		else {
			printf("-");
			m_cursys->ProcessEMM(m_curprg.CA_System[m_curecmindex].EMM,emminfo.provId,&_Data[_Data[0]+1]);
			m_logger.LogData(_Data,myDataLen,cLogger::EMM_Text);
			m_logger.LogData(_Data,myDataLen,cLogger::EMM_Bin);
			emmTimeout = 100000;
			++m_emmcount;
		}

		if ((m_emmcount > emmTimeout) && m_StopEmm_OnChange )
		{
			printf("[ERROR] EMM Filter Time Out.\n");
			StopEMMFilter();
			ClearAllData();
			//SignalStopFilter(EVENT_STOPEMMFILTER);
		}
//******************************************************** (10/17/2006) **
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//Private member variables
//------------------------------------------------------------------------------
void DllClass::DoEvents()
{
	for (MSG msg; ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); )
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void DllClass::On_Filter_Data_Arrival(int MyFilter, int DataLen, unsigned char *_Data)
{
	FILTERDATA d;
	int DataPtr = 0;
	int errorCnt = 0;
	bool PacketBreak = false;
	bool verifyPacket = false;
	bool SendPacket = false;
	int pktLen = 0;
	int ptr = 0;
	int RunLength = 0;
	int CheckLen = 0;
	int sendPtr = 0;
	int providerID = 0;
	int PacketLen = 0;

//*******************************************************
// Special EMM processing to improve from 89% error rate 
//*******************************************************
	if (MyFilter == EMM_FILTER_ID) {
		if (m_use_max_parser == true) {
		  //Set ptr to last used location (int EMMbufptr)
			ptr = EMMbufptr;
			EMMbufptr=0;
			RunLength = DataLen+ptr;
		  //Add New Data to EMM Filter Buffer (unsigned char* EMMbuffer[1200])
			for(int it=ptr; it<RunLength; it++) {
				EMMbuffer[it] = _Data[it-ptr];
			}
			memset(&EMMbuffer[RunLength],0xFF,(1199-RunLength));

		  //Process for packets...
			for(DataPtr = 0; DataPtr<RunLength; ) {
			  //Reset Send Packet
				SendPacket = false;

			  //Check for start of new series
				PacketBreak = false;
				while ( EMMbuffer[DataPtr] == 0xFF && DataPtr < RunLength ) {
					DataPtr++;
					PacketBreak = true;
				}
				if (DataPtr >= RunLength) 
					return;
				if ( PacketBreak == true &&
					EMMbuffer[DataPtr] == 0x00) {
					DataPtr++;
				}

			  //Packet start ???
				verifyPacket = false;
				while ( verifyPacket == false && DataPtr < RunLength) {
					providerID = EMMbuffer[DataPtr+10]*256+EMMbuffer[DataPtr+11];
					if ((( EMMbuffer[DataPtr+0] == 0x82 || 
						   EMMbuffer[DataPtr+0] == 0x83 ) && 
						   EMMbuffer[DataPtr+1] == 0x70 ) &&
						 ( providerID == 0x0001 ||
						 ( providerID >= 0x0101 && providerID <= 0x010F ) || 
						   providerID == 0x0501 ||
						   providerID == 0x0801 ||
						 ( providerID >= 0x0901 && providerID <= 0x090F ))) {
							  verifyPacket = true;
					} else {
						DataPtr++;
						errorCnt++;
						if (errorCnt > 460) {
							DataPtr=RunLength;
							firstEMMpacket = false;
						}
					}
					DoEvents();
				}
				if (DataPtr >= RunLength) 
					return;

              //Check how many bytes we have left
				CheckLen = RunLength-DataPtr;
			    PacketLen = (EMMbuffer[DataPtr+9]+10);
				if (CheckLen < (PacketLen+1)) {	//or myDataLen
					EMMbufptr = CheckLen;
					memcpy(EMMbuffer,&EMMbuffer[DataPtr],EMMbufptr);
					return;
				} else if (verifyPacket == true) {
				  //Reset Filter Buffer
					memset(EMMfilter,0xFF,myDataLen);
				  //look for what we want
					m_count++; m_count %= 4;
					Turn(m_count);
					switch(EMMbuffer[DataPtr]) {
					  // (82706C) EMM-G Packets
						case 0x82:
							sendPtr=DataPtr;
							DataPtr+=PacketLen;
							SendPacket = true;
							break;
						case 0x83:
							if (EMMbuffer[DataPtr+2] == 0x94) {
						  // (837094) S0x cards & (83706C) 10x cards EMM-S Packets
//								sendPtr=DataPtr;
							  //Skip this packet for now
    							DataPtr+=PacketLen;
							} else if (EMMbuffer[DataPtr+2] == 0x6C && m_process_emm_s == true) {
						  //83706C - Process
								for (int is=0; is<keys.EMMptr; is++) {
									if ( EMMbuffer[DataPtr+5] == keys.EMMs[is][0] &&
										 EMMbuffer[DataPtr+4] == keys.EMMs[is][1] &&
										 EMMbuffer[DataPtr+3] == keys.EMMs[is][2] ) {
											 printf("!");
											is=keys.EMMptr;
											sendPtr=DataPtr;
											SendPacket = true;
									}
								}
    							DataPtr+=PacketLen;
							} else if (EMMbuffer[DataPtr+2] == 0x6C && m_process_emm_s == false) {
						  //83706C - DO NOT Process
    							DataPtr+=PacketLen;
							} else {
								DataPtr++;
								SendPacket = false;
							}
							break;
					  //Not what we want check for next packet
						default:
							DataPtr++;
							SendPacket = false;
							break;
					}
					if (SendPacket == true) {
					  //Put leader on packet
						EMMfilter[0] = 0x00;
					  //Check for bad packet
						if (EMMbuffer[sendPtr+PacketLen] != 0x83 &&
							EMMbuffer[sendPtr+PacketLen] != 0xFF) {
						  //Bad Packet - Look for bad byte
						  //Raw Packet:  0082706Cxxxxxxxxxxxxxxxxxx {nn} 810010...
						  //Extra byte is not extra, it is how long remainer of packet is
							int PKlenByte = 0x6F;
							for (int is=0; is<PacketLen+1; is++) {
								if (EMMbuffer[sendPtr+is] == PKlenByte) {
									memcpy(&EMMfilter[is+1],&EMMbuffer[sendPtr+is+1],(PacketLen-is));
									is=PacketLen+1;
								} else {
									EMMfilter[is+1] = EMMbuffer[sendPtr+is];
									PKlenByte--;
								}
							}
						} else {
						  //Good Packet
							memcpy(&EMMfilter[1],&EMMbuffer[sendPtr],PacketLen+1);
						}
						d.FilterID=MyFilter;
						memcpy(d.Data,&EMMfilter[0],myDataLen);
						m_EMMq.push(d);
					}
				}
			}
			return;
//*******************************************************
//*******************************************************
		} else {

#ifdef THREADED_DATA_FLOW
			for(int i=0;i<DataLen/myDataLen;i++) {
				d.FilterID=MyFilter;
				memcpy(d.Data,&_Data[myDataLen*i],myDataLen);

				m_count++; m_count %= 4;
				Turn(m_count);
				if ( d.Data[0] == 0x00 && d.Data[1] == 0x82 && 
					d.Data[2] == 0x70 && d.Data[3] == 0x6C ) {
					m_EMMq.push(d);
				} 
				else if (keys.EMMptr > 0 && d.Data[0] == 0x00 && d.Data[1] == 0x83) {
					for (int is=0; is<keys.EMMptr; is++) {
						if ( d.Data[6] == keys.EMMs[is][0] &&
							 d.Data[5] == keys.EMMs[is][1] &&			   
							 d.Data[4] == keys.EMMs[is][2] ) {
								 printf("!");
					//printf("(%02X%02X%02X%02X %02X%02X)\n",d.Data[6],d.Data[5],d.Data[4],d.Data[7],d.Data[11],d.Data[12]);
								m_EMMq.push(d);
								is=keys.EMMptr;
						}
					}
				} 
			}
#else
			ProcessEMMData(&d);
			UpdatePIDInfo(EMM_FILTER_ID);
#endif
			return;
		}
	}
//*******************************************************
//*******************************************************

	//Make Sure it's a good packet
	if (DataLen%myDataLen == 0) {
		//break into smaller 184 byte packets
		for(int i=0;i<DataLen/myDataLen;i++)
		{
			d.FilterID=MyFilter;
			memcpy(d.Data,&_Data[myDataLen*i],myDataLen);
			switch(d.FilterID)
			{
			case DATA_FILTER_ID:
#ifdef THREADED_DATA_FLOW
				m_PMTq.push(d);
#else
				ProcessPMTData(&d);
				UpdatePIDInfo(DATA_FILTER_ID);
#endif
				break;
			case EMM_FILTER_ID:
			case ECM_FILTER_ID:
#ifdef THREADED_DATA_FLOW
				m_ECMq.push(d);
#else
			ProcessECMData(&d);
			UpdatePIDInfo(ECM_FILTER_ID);
#endif
				break;
			};
		}
	} else {
		d(printf("{BP %d}",DataLen));
	}
}
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////
//Globals																  //
////////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllAboutDlgFunc(HWND hDlg,UINT message,UINT wParam,LONG lParam)
{
	switch (message)
	{
	case WM_INITDIALOG:
		break;
	case WM_COMMAND:
		if ((LOWORD(wParam) == ID_OK ) || (LOWORD(wParam) == IDCANCEL ))
		{
			EndDialog(hDlg,TRUE);
		}
		break;
	}
	return(FALSE);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void APIENTRY GetDlgPosition(HWND hDlg, LPCTSTR lpszEntry)
{
	long LeftSave, TopSave = 0;
	char strEntry[255];
	HWND hwndDeskTop;
	RECT rcDeskTop;
	hwndDeskTop = ::GetDesktopWindow();		// Get a handle to the desktop window
	GetWindowRect(hwndDeskTop, &rcDeskTop);	// Get dimension of desktop in a rect
	GetWindowRect(hDlg,&rc);				// Get dimension of Window in a rect

	sprintf(strEntry,"%sLeft",lpszEntry);
	LeftSave = hklm.GetProfileInt(sSection, _T(strEntry), 0);
	if (LeftSave < 0) {
		LeftPos = 0;
	} else if (LeftSave > (rcDeskTop.right-LeftSave)) {
		LeftPos = (rcDeskTop.right-(rc.right-rc.left));
	} else {
		LeftPos = LeftSave;
	}

	sprintf(strEntry,"%sTop",lpszEntry);
	TopSave = hklm.GetProfileInt(sSection, _T(strEntry), 0);
	if (TopSave < 0) {
		TopPos = 0;
	} else if (TopSave > (rcDeskTop.bottom-TopSave-15)) {
		TopPos = (rcDeskTop.bottom-(rc.bottom-rc.top-15));
	} else {
		TopPos = TopSave;
	}
}

void APIENTRY SaveDlgPosition(HWND hDlg, LPCTSTR lpszEntry)
{
	BOOL bSuccess = false;
	char strEntry[255];
	HWND hwndDeskTop;
	RECT rcDeskTop;
	hwndDeskTop = ::GetDesktopWindow();		// Get a handle to the desktop window
	GetWindowRect(hwndDeskTop, &rcDeskTop);	// Get dimension of desktop in a rect
	GetWindowRect(hDlg,&rc);				// Get dimension of Window in a rect

	if (rc.left < 0) {
		LeftPos = 0;
	} else if ((rc.right-rc.left) > (rcDeskTop.right-rc.left)) {
		LeftPos = (rcDeskTop.right-(rc.right-rc.left));
	} else {
		LeftPos = rc.left;
	}
	sprintf(strEntry,"%sLeft",lpszEntry);
	bSuccess = hklm.WriteProfileInt(sSection, _T(strEntry), LeftPos);

	if (rc.top < 0) {
		TopPos = 0;
	} else if ((rc.bottom-rc.top+15)>(rcDeskTop.bottom-rc.top-15)) {
		TopPos = (rcDeskTop.bottom-(rc.bottom-rc.top-15));
	} else {
		TopPos = rc.top;
	}
	sprintf(strEntry,"%sTop",lpszEntry);
	bSuccess = hklm.WriteProfileInt(sSection, _T(strEntry), TopPos);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL APIENTRY DllMonitorDlgFunc(HWND hDlg,UINT message,UINT wParam,LONG lParam)
{
	static HMENU DVBCoreMenu=NULL;
	switch (message)
	{
	case WM_INITDIALOG:
		{
			char txt[1024]="";
			char item[256]="";
			if (m_DLLInstance.DVBCoreFlag) {
				sprintf(txt,"DVBCore");
				DVBCoreMenu=LoadMenu(m_DLLInstance.GetDLLInstance(),"DVBCOREMENU");
				SetMenu(hDlg,DVBCoreMenu);
			} else {
				if(g_API_Impl->MDAPIFlag())
					sprintf(item,"MDAPI (8.2): %02d.%02d  ",g_API_Impl->MDVersionMajor(),g_API_Impl->MDVersionMinor());
				else
					sprintf(item,"MDAPI (<=8.1b)  ");
				strcpy(txt,item);
				if(g_API_Impl->WTVPAPIFlag())
					sprintf(item,"WTVP API: %02d.%02d  ",g_API_Impl->WTVPVersionMajor(),g_API_Impl->WTVPVersionMinor());
				else
					sprintf(item,"");
				strcat(txt,item);
				if(g_API_Impl->PROGAPIFlag())
				{
					sprintf(item,"PROG API: %02d.%02d  ",g_API_Impl->PROGVersionMajor(),g_API_Impl->PROGVersionMinor());
					strcat(txt,item);
				}
			}

			GetDlgPosition(hDlg, "MonitorDlg");
			SetWindowPos(hDlg,NULL,LeftPos,TopPos,rc.right-rc.left,rc.bottom-rc.top+15,
			SWP_NOZORDER|SWP_NOACTIVATE /*|SWP_NOSIZE|SWP_NOMOVE */);

			m_DLLInstance.m_hwnd_monitor=hDlg;
			SetDlgItemText(hDlg,IDC_VERSION,txt);
			SetDlgItemText(hDlg,IDC_SYS,m_DLLInstance.m_infotext);
			SetDlgItemText(hDlg,IDC_KEY,m_DLLInstance.m_keystr);
			SetDlgItemText(hDlg,IDC_STC_PID,"PMT:0x0000");
			SetDlgItemText(hDlg,IDC_PMT_DATAFLOW," ");
			SetDlgItemText(hDlg,IDC_PMT_DATA," ");
			SetDlgItemText(hDlg,IDC_ECM,"ECM:0x0000");
			SetDlgItemText(hDlg,IDC_DATAFLOW," ");
			SetDlgItemText(hDlg,IDC_CA,"CA:0x0000");
			SetDlgItemText(hDlg,IDC_ECM_DATA,"DATA: None");
			SetDlgItemText(hDlg,IDC_KEY,"Key: None");
			SetDlgItemText(hDlg,IDC_EMM,"EMM:0x0000");
			SetDlgItemText(hDlg,IDC_PROCESS," ");
			if (m_DLLInstance.m_isemmfilter) {
				SetDlgItemText(hDlg,IDC_EMM_STATUS,"<== Waiting for EMM Packet...");
			} else {
				SetDlgItemText(hDlg,IDC_EMM_STATUS,"EMM-G filter: OFF");
			}
			SetDlgItemText(hDlg,IDC_EMM_DATA,"DATA: None");
			m_DLLInstance.InitializeMenu();
			char line[256];
			FILE* f=fopen("emulog.txt","r");
			if(f)
			{
				while(fgets(line,sizeof(line),f))
				{
					line[strlen(line)-1]='\0';
					SendMessage(GetDlgItem(hDlg,IDC_LST_MSG),LB_ADDSTRING,0,(LPARAM)line);

				}
				int count=SendMessage(GetDlgItem(hDlg,IDC_LST_MSG),LB_GETCOUNT,0,0);
				if(count>8)
					SendMessage(GetDlgItem(hDlg,IDC_LST_MSG),LB_SETTOPINDEX,(WPARAM)count-8,0);
			}
			CheckDlgButton(hDlg,IDC_AUTO_SCROLL,MF_CHECKED);
			m_DLLInstance.autoScroll=true;
		}
		break;
	case WM_COMMAND:
		if ((LOWORD(wParam) == ID_OK ) || (LOWORD(wParam) == IDCANCEL ))
		{
			SaveDlgPosition(hDlg, "MonitorDlg");
			m_DLLInstance.m_hwnd_monitor=NULL;
			if (m_DLLInstance.DVBCoreFlag)
				DestroyMenu(DVBCoreMenu);
			EndDialog(hDlg,TRUE);
		}
		if (LOWORD(wParam) == IDC_AUTO_SCROLL ) {
			if (IsDlgButtonChecked(hDlg,IDC_AUTO_SCROLL) == BST_CHECKED) {
				m_DLLInstance.autoScroll = true;
			} else {
				m_DLLInstance.autoScroll = false;
			}
			break;
		}

        if(m_DLLInstance.DVBCoreFlag)
            switch(LOWORD(wParam))
            {
				case IDM_ABOUT:
				case IDM_ACTIVE:
				case IDM_BEEP_ON_KEY:
				case IDM_NORTH_AMERICA:
				case IDM_ECM_ACTIVE:
				case IDM_EMM_ACTIVE:
				case IDM_HIDE_BAD_MESSAGE:
				case IDM_KEEP_KEYS:
				case IDM_LOADKEYS:
				case IDM_LOAD_MONITOR:
				case IDM_LOGCAMID:
				case IDM_LOGCW:
				case IDM_LOGE3:
				case IDM_LOGECMBIN:
				case IDM_LOGECMTEXT:
				case IDM_LOGEMMBIN:
				case IDM_LOGEMMTEXT:
				case IDM_LOGEMM_LOGBYCAMID:
				case IDM_LOG_MONITOR:
				case IDM_LOG_CONSOLE:
				case IDM_LOGEMM_LOGBYSINGLEFILE:
				case IDM_LOGEMM_LOGBYTWOFILES:
				case IDM_LOGEMM_OFF:
				case IDM_LOGGER:
				case IDM_LOG_EMM_G:
				case IDM_PROCESS_EMM_S:
				case IDM_SHOW:
				case IDM_SHOW_CONSOLE:
				case IDM_SHOW_DISASSEMBLY_BASIC:
				case IDM_SHOW_DISASSEMBLY_DEBUG:
				case IDM_SHOW_DISASSEMBLY_VERBOSE:
				case IDM_USE_MAX_PARSER:
					m_DLLInstance.On_Menu_Select(LOWORD(wParam));
                break;
            };
		break;

	case WM_SYSCOMMAND:
		if (wParam==SC_CLOSE)
		{
			SaveDlgPosition(m_DLLInstance.m_hwnd_monitor, "MonitorDlg");
			m_DLLInstance.m_hwnd_monitor=NULL;
			EndDialog(hDlg,TRUE);
		}
		break;
	}

	return(FALSE);
}
//------------------------------------------------------------------------------

