//

#include "stdafx.h"

///////////////////////////////////////////////////////////////////////////////////////
// 내부에서 필요한 정보를 정의한다.
///////////////////////////////////////////////////////////////////////////////////////
#define READ_RETRY_COUNT		10			// 최대 10번정도 읽기를 수행한다.
#define DEFAULT_READ_COUNT		32			// 32바이트만 읽는다.

#define SZ_MAX_QUEUE			1024

#define LOG_DATA

#define DEVICE_CONTEXT		0x1450
#define OPEN_CONTEXT		0x1451

///////////////////////////////////////////////////////////////////////////////////////
// 전역변수를 정의한다.
///////////////////////////////////////////////////////////////////////////////////////
/*
 * 기본적인 전역변수를 정의한다.
 */
DWORD	dwDebugging = 0;
HANDLE	hComm = INVALID_HANDLE_VALUE;
DWORD	dwNateGPSPort = 1;
BOOL	IsPowerOn = FALSE;

/*
 * NMEA 메시지 변환을 위한 전역변수를 선언한다.
 */
char	szReadQueue[SZ_MAX_QUEUE+1];		// GPS로부터 수신한 데이터를 저장하는 데이터 큐
int		nReadQueueLen = 0;
char	szDataQueue[SZ_MAX_QUEUE+1];		// 호출 프로세스에게 전달할 컨버전된 데이터를 저장하는 데이터 큐 
int		nDataQueueLen = 0;

///////////////////////////////////////////////////////////////////////////////////////
// 내부에서 사용되는 함수를 정의한다.
///////////////////////////////////////////////////////////////////////////////////////

/*
 * 로그 정보를 저장한다.
 */
void Log(char *szStr, bool bLF = true)
{
	if(dwDebugging) {
		FILE *f = fopen("\\NateNMEADriverLog.txt", "a");
		//fprintf(f, "%u", GetTickCount());
		fprintf(f, "%s", szStr);
		if (bLF)
			fprintf(f, "\n");
		fflush(f);
		fclose(f);
	}
}

/*
 * 내부에서 사용되는 모든 변수를 초기화 한다.
 */
void	InitData()
{
	memset(szReadQueue, 0, SZ_MAX_QUEUE+1);
	memset(szDataQueue, 0, SZ_MAX_QUEUE+1);
	nReadQueueLen = 0;
	nDataQueueLen = 0;
}

/*
 * GPS 모듈로부터 데이터를 읽어온다.
 */
DWORD	ReadDataFromGPS(char *szReadBuffer)
{
	int		nReadRetryCount = READ_RETRY_COUNT;
	char	*pReadBuffer = szReadBuffer;
	DWORD	dwReadBytes = 0;
	DWORD	dwTotalReadBytes = 0;

	// 변수를 초기화 한다.
	if(nReadQueueLen > 0) {
		// 데이터 큐의 내용을 복사한다.
		memcpy(szReadBuffer, szReadQueue, nReadQueueLen);
		pReadBuffer += nReadQueueLen;
		dwTotalReadBytes += nReadQueueLen;
		nReadQueueLen = 0;
		memset(szReadQueue, 0, SZ_MAX_QUEUE+1);
	}

	// 데이터를 GPS로부터 읽는다.
	char	*pLineEnd = NULL;
	for(int i = 0; i < nReadRetryCount; i++) {
		ReadFile(hComm, pReadBuffer, DEFAULT_READ_COUNT, &dwReadBytes, NULL);

		// 파워온 된경우 아무것도 하지 않는다.
		if(IsPowerOn) {
			IsPowerOn = FALSE;
			memset(szReadBuffer, 0, SZ_MAX_QUEUE+1);
			return 0;
		}
		
		// 읽은 데이터가 0 인경우 빠져나간다.
		if(dwReadBytes == 0 && dwTotalReadBytes == 0)
			break;

		// 읽은 사이즈 정보를 적용한다.
		pReadBuffer += dwReadBytes;
		dwTotalReadBytes += dwReadBytes;

		// 읽은 데이터에 라인 시작이 있는지 확인한다.
		char *pLineStart = strchr(szReadBuffer+1, '$');
		if(pLineStart != NULL) {
			// 읽은 데이터의 일부를 읽기 큐에 넣는다.
			nReadQueueLen = strlen(pLineStart);
			memcpy(szReadQueue, pLineStart, nReadQueueLen);
#ifdef LOG_DATA
			char buf[512];
			sprintf(buf, "Read Data (%d-%d) =>\n[%s]", strlen(szReadBuffer), nReadQueueLen, szReadBuffer);
			Log(buf);
#endif
			*pLineStart = '\0';
			dwTotalReadBytes -= nReadQueueLen;
			break;
		}

		// 만일 루핑카운트 만큼 $를 받지 못했다면 더 루핑을 돈다.
		if((i+1) == nReadRetryCount)
			nReadRetryCount++;

		// 읽은 내용이 크면 문제가 있는 것이므로 빠져나간다. (그냥 상위로 보낸다.)
		if(dwTotalReadBytes > 256)
			break;

		// 데이터가 없을 것에 대비해서 0.05초를 쉰다.
#if 0
		Sleep(50);
#endif
	}

	// 읽은 데이터 사이즈를 리턴한다.
	return dwTotalReadBytes;
}

/*
 * 위도정보를 계산한다.
 */
//$GPGGA,102736.272,3051569,N,3572981,E,1,06,1.6,24.4,M,25.3,M,0.0,0000*7D
//$GPRMC,102736.272,A,3051569,N,3572981,E,28.4,182.3,080605,,*06
//
//위에 열거한 문자열은 NATE DRIVE의 출력 형태이다.
//이중 변경 되어야할 문자열은 GPGGA,GPRMC이다,나머지는 그냥 보낸다.
//특히 두문자열중 N과 E앞의 7자리는 위도와 경도를 나타내는데
//이 문자열들은 변형된 데이타 들이다.
//우리는 이 변형된 데이타를 원상태로 회복을 해야 한다.
//위도 계산 (3051569) :
//  1. 위도 데이타의 뒤 4자리(1569)는 1/100 단위이며, 지도는 1/60단위 이므로
//     60을 곱하여 1/60형태로 변환 한다.(계산의 정확도를 위해 소수점이하로
//     변환 함이 좋을듯 하다 0.1569로 변환 해서 계산한다)
//     여기서 모든 계산은 4자리 마무리 되어야 하므로 100을 곱하여 소수점 이하 두자리를 취한다.
//     지도상의 위치가 계산이 되었으면 초단위 이하 보정 값인 1900을 더한다.
//
//       예) 1569 / 10000 = 0.1569 x 60 = 9.414 x 100 = 941.4 + 1900 = 2841.4 / 1 = 2841
//
//     계산 값이 6000보다 크면 자리 올림을 하고 6000을 빼준다.
//
//       예) 6700이면 6700-6000 = 0700 이고 이후 도단위에서 1을 더해 준다.
//
//  빼준 값은 지도 상의 데이타이지만 네비에서 gps의 신호를 받아서 다시
//  60을 곱하여 주므로 GPS 포멧을 맞추기 위해 60을 나누어 준다.
//  자리수를 4자리를 맞추기 위해100을 곱한다.
//  예) 위결과값 2841 x 60 = 47.35 X 100 = 4735
//  이제 위도의읽어 들인 앞 3자리를 더한다.
//  예) 3050000+4735 = 3054735
//  마지막으로 32000000은 도단위 이상의 보정값이다.이를 결과 값과더한다.
//  예) 32000000 + 3054735 = 35054735 만약 자리올림이 발생 하면 35054735 + 10000 한다.
//  모든 계산이 끝났으면 ASCII 형태로출력을 하는데 이때의 형태는 3505.4735 이다.
//  경도의 계산은 32000000 대신 125000000을 더하면 된다.

char *CalcLatitude(char *data) 
{
	static char	szLat[256];
	char	szHigh[10], szLow[5];
	float	nfLow = 0.0;
	int		nLat = 0;
	int		temp = 0;

	if(strlen(data) != 7) {
		strcpy(szLat, data);
	} else if(strcmp(data, "0000000") == 0) {
		strcpy(szLat, "0000.0000");
	} else {
		// 위도 값을 구분한다.
		strcpy(szLow, data+3);
		strncpy(szHigh, data, 3);
		strcpy(szHigh+3, "0000");

		// 위도 값을 계산한다.
		nLat = atol(szHigh);
		nLat += 32000000;
		nfLow = (float)atoi(szLow)/10000.0;
		nfLow *= 60.0;
		nfLow *= 100.0;
		nfLow += 1900;
		nfLow /= 1.0;
		if(nfLow >= 6000.0) {
			nLat += 10000;
			nfLow -= 6000.0;
		}
		nfLow /= 60.0;
		nfLow *= 100.0;
		temp = (int)(nfLow * 10.0);
		temp %= 10;
		if(temp > 5)	// 반올림한다.
			nfLow += 1;

		nLat += (int) nfLow;

		sprintf(szLat, "%d.%04d", nLat/10000, nLat%10000);
	}

	return szLat;
}

/*
 * 경도를 계산한다.
 */
char *CalcLongitude(char *data) 
{
	static char	szLong[256];
	char	szHigh[10], szLow[5];
	float	nfLow = 0.0;
	int		nLong = 0;
	int		temp = 0;

	if(strlen(data) != 7) {
		strcpy(szLong, data);
	} else if(strcmp(data, "0000000") == 0) {
		strcpy(szLong, "00000.0000");
	} else {
		// 경도 값을 구분한다.
		strcpy(szLow, data+3);
		strncpy(szHigh, data, 3);
		strcpy(szHigh+3, "0000");

		// 경도 값을 계산한다.
		nLong = atol(szHigh);
		nLong += 125000000;
		nfLow = (float)atoi(szLow)/10000.0;
		nfLow *= 60.0;
		nfLow *= 100.0;
		nfLow += 1900;
		nfLow /= 1.0;
		if(nfLow >= 6000.0) {
			nLong += 10000;
			nfLow -= 6000.0;
		}
		nfLow /= 60.0;
		nfLow *= 100.0;
		temp = (int)(nfLow * 10.0);
		temp %= 10;
		if(temp > 5)	// 반올림한다.
			nfLow += 1;

		nLong += (int) nfLow;

		sprintf(szLong, "%d.%04d", nLong/10000, nLong%10000);
	}

	return szLong;
}

/*
 * 체크섬을 계산한다.
 */
// CHECKSUM
// 모든 데이타는 CHECKSUM을 가지는데 이는 NMEA표준이며
// $  * 사이의 ($,*제외)의 모든 문자(공백및 특수기호 포함)를 XOR한다.
// 이때 값이 16진수이며, 출력은 ASCII문자료 출력 해야한다.
// 만약 16진수 0A이면 LINEFEED 신호가 출력이 되지만 ASCII 문자 "0A"로 출력을
// 해야 한다. CHECKSUM 값이 틀리면 모든 네비에서는 값을 읽지 않는다.

char *CalcChecksum(char *data)
{
	static char szChecksum[5];
	unsigned char	sum = 0;

	int	nLen = strlen(data);
	sum = (unsigned char)*data;
	for(int i = 1; i < nLen; i++) {
		sum ^= (unsigned char)*(data+i);
	}

	sprintf(szChecksum, "%02x", sum);
	strcpy(szChecksum, _strupr( szChecksum ));

	return szChecksum;
}

/*
 * 읽어온 데이터를 파싱하여 데이터 내용을 변환한다.
 */
DWORD	ConvertNMEAData(char *szData)
{
	DWORD	nDataLen = 0;
	BOOL	IsGGA = FALSE, IsRMC = FALSE;

	// 버퍼의 데이터 사이즈를 확인한다.
	if(*szData == '\0')
		return 0;

	// NMEA Message ID를 판독한다.
	if(_strnicmp(szData, "$GPGGA", 6) == 0) 
		IsGGA = TRUE;
	else if(_strnicmp(szData, "$GPRMC", 6) == 0) 
		IsRMC = TRUE;
	else 
		nDataLen = strlen(szData);

	// GPGGA, GPRMC에 대한 처리를 수행한다.
	// $GPGGA,004109.133,4216912,N,2191488,E,1,06,1.4,88.9,M,21.4,M,0.0,0000*76
	// => $GPGGA,004109.133,3622.0078,N,12719.4654,E,1,06,1.4,88.9,M,21.4,M,0.0,0000*47
	// $GPRMC,004109.133,A,4216912,N,2191488,E,0.0,272.0,250906,,*35
	// => $GPRMC,004109.133,A,3622.0078,N,12719.4654,E,0.0,272.0,250906,,*04
	if(IsGGA || IsRMC) {
		char	szChecksum[SZ_MAX_QUEUE+1];
		char	*pszChecksum = szChecksum;
		char	szLatitude[256];
		char	*pszLatitude = szLatitude;
		char	szLongitude[256];
		char	*pszLongitude = szLongitude;
		char	szConvertData[SZ_MAX_QUEUE+1];
		char	*pszConvertData = szConvertData;
		int		nConvertDataLen = 0;
		char	*pszData = szData;
		int		nDataOption = 0;

		// 변수를 초기화 한다.
		memset(szConvertData, 0, SZ_MAX_QUEUE+1);
		memset(szChecksum, 0, SZ_MAX_QUEUE+1);
		memset(szLatitude, 0, 256);
		memset(szLongitude, 0, 256);

		// 컨버전을 수행한다.
		while(*pszData != '\0') {
			// 파워온 된경우 아무것도 하지 않는다.
			if(IsPowerOn) {
				IsPowerOn = FALSE;
				return 0;
			}
		
			if((nDataOption == 2 && IsGGA) || (nDataOption == 3 && IsRMC) ) {	// Latitude
				if(*pszData == ',') {
					nDataOption++;

					// Latitude를 계산한다.
					char *pLat = CalcLatitude(szLatitude);
					sprintf(pszConvertData, "%s,", pLat);
					pszConvertData += strlen(pszConvertData);

					// 계산된 정보를 체크섬에 넣는다.
					sprintf(pszChecksum, "%s,", pLat);
					pszChecksum += strlen(pszChecksum);
				} else {
					// Latitude를 백업한다.
					*pszLatitude = *pszData;
					pszLatitude++;
				}
			} else if((nDataOption == 4 && IsGGA) || (nDataOption == 5 && IsRMC)) {	// Longitude
				if(*pszData == ',') {
					nDataOption++;

					// Longitude를 계산한다.
					char *pLong = CalcLongitude(szLongitude);
					sprintf(pszConvertData, "%s,", pLong);
					pszConvertData += strlen(pszConvertData);

					// 계산된 정보를 체크섬에 넣는다.
					sprintf(pszChecksum, "%s,", pLong);
					pszChecksum += strlen(pszChecksum);
				} else {
					// Longitude를 백업한다.
					*pszLongitude = *pszData;
					pszLongitude++;
				}
			} else if(*pszData == ',') {	// 데이터 구분을 카운팅 한다.
				nDataOption++;
				*pszConvertData = *pszData;
				pszConvertData++;
				*pszChecksum = *pszData;
				pszChecksum++;
			} else if(*pszData == '*') {
				*pszConvertData = *pszData;
				pszConvertData++;

				// 체크섬을 계산한다.
				char *pCSum = CalcChecksum(szChecksum);
				strcpy(pszConvertData, pCSum);
				pszConvertData += strlen(pszConvertData);

				pszData += 2;
			} else {
				// 데이터를 복사한다.
				*pszConvertData = *pszData;
				pszConvertData++;
				if(*pszData != '$' && *pszData != 0x0d && *pszData != 0x0a) {
					*pszChecksum = *pszData;
					pszChecksum++;
				}
			}

			// 버퍼를 이동한다.
			pszData++;
		}

		// 컨버전된 데이터를 복사한다.
		nDataLen = strlen(szConvertData);
		memset(szData, 0, SZ_MAX_QUEUE+1);
		memcpy(szData, szConvertData, nDataLen);
#ifdef LOG_DATA
		char buf[512];
		sprintf(buf, "Convert Data (%d)=>\n[%s]", nDataLen, szConvertData);
		Log(buf);
#endif
	}

	return nDataLen;
}

///////////////////////////////////////////////////////////////////////////////////////
// 외부에서 호출되는 함수를 정의한다.
///////////////////////////////////////////////////////////////////////////////////////

NATENMEADLL_API DWORD	COM_Init(LPCTSTR pContext, LPCVOID lpvBusContext);
NATENMEADLL_API BOOL	COM_Deinit( DWORD hDeviceContext );
NATENMEADLL_API DWORD	COM_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );
NATENMEADLL_API BOOL	COM_Close( DWORD hOpenContext );
NATENMEADLL_API BOOL	COM_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut );
NATENMEADLL_API void	COM_PowerUp( DWORD hDeviceContext );
NATENMEADLL_API void	COM_PowerDown( DWORD hDeviceContext );
NATENMEADLL_API DWORD	COM_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );
NATENMEADLL_API DWORD	COM_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );
NATENMEADLL_API DWORD	COM_Seek( DWORD hOpenContext, long Amount, WORD Type );

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	switch ( ul_reason_for_call ) {
		case DLL_PROCESS_ATTACH:
			break;
		case DLL_PROCESS_DETACH:
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
	}

	return TRUE;
}

NATENMEADLL_API DWORD COM_Init( LPCTSTR pContext, LPCVOID lpvBusContext)
{
	// 초기화 처리한다.
	InitData();

#ifdef LOG_CALLS
	Log("Init");
#endif
	DWORD dwPort = 0;
	DWORD dwDebug = 0;
	HKEY hKey;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pContext, 0, 0, &hKey) == ERROR_SUCCESS)
	{
		TCHAR szDriverKey[255];
		DWORD dwSize = 255;
		DWORD dwType;
		if (RegQueryValueEx(hKey, L"Key", 0, &dwType, (BYTE *)szDriverKey, &dwSize) == ERROR_SUCCESS)
		{
			RegCloseKey(hKey);
			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDriverKey, 0, 0, &hKey) == ERROR_SUCCESS)
			{
				dwSize=4;
				if (RegQueryValueEx(hKey, L"NateGPSPort", 0, &dwType, (BYTE *)&dwPort, &dwSize) != ERROR_SUCCESS)
					dwPort = 0;
				dwSize=4;
				if (RegQueryValueEx(hKey, L"Debug", 0, &dwType, (BYTE *)&dwDebug, &dwSize) != ERROR_SUCCESS)
					dwDebug = 0;
				RegCloseKey(hKey);
			}
		}
	}
	if (dwPort != 0)
		dwNateGPSPort = dwPort;
	if(dwDebug != 0)
		dwDebugging = dwDebug;

	return DEVICE_CONTEXT;
}

NATENMEADLL_API BOOL COM_Deinit( DWORD hDeviceContext )
{
	if (hDeviceContext != DEVICE_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False Deinst");
#endif
		return FALSE;
	}

	// 초기화 처리한다.
	InitData();

#ifdef LOG_CALLS
	Log("DeInit");
#endif
	return TRUE;
}

NATENMEADLL_API DWORD COM_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode )
{
	if (hDeviceContext != DEVICE_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False Open");
#endif
		return 0;
	}
	TCHAR szPort[255];
	swprintf(szPort, L"COM%u:", dwNateGPSPort);

	hComm = CreateFile(
		szPort, 
		AccessCode,
		ShareMode,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);

#ifdef LOG_CALLS
	char buf[255];
	sprintf(buf, "Open port %S for access %u and share %u %s", szPort, AccessCode, ShareMode, hComm ? "OK" : "Failed");
	Log(buf);
#endif

	if (hComm == INVALID_HANDLE_VALUE)
		return 0;
	else {
		// 초기화 처리한다.
		InitData();

		return OPEN_CONTEXT;		
	}
}

NATENMEADLL_API BOOL COM_Close( DWORD hOpenContext )
{
	if (hOpenContext != OPEN_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False Close");
#endif
		return 0;
	}

#ifdef LOG_CALLS
	Log("Close");
#endif
	BOOL bRet = CloseHandle(hComm);
	hComm = INVALID_HANDLE_VALUE;

	// 초기화 처리한다.
	InitData();

	return bRet;
}

NATENMEADLL_API BOOL COM_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )
{
	if (hOpenContext != OPEN_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False IOControl");
#endif
		return 0;
	}

#ifdef LOG_CALLS
	char buf[255];
	sprintf(buf, "IOControl code=%u, inlen=%u", dwCode, dwLenIn);
	Log(buf);
#endif

	BOOL bRet=DeviceIoControl(
		hComm, 
		dwCode, 
		pBufIn, 
		dwLenIn, 
		pBufOut, 
		dwLenOut,
		pdwActualOut, 
		NULL);

#ifdef LOG_CALLS
	sprintf(buf, "IOControl return bRet=%d, outlen=%u", bRet, *pdwActualOut);
	Log(buf);
#endif

	return bRet;
}

NATENMEADLL_API void COM_PowerUp( DWORD hDeviceContext )
{
	// 초기화 처리한다.
	InitData();
	IsPowerOn = TRUE;
}

NATENMEADLL_API void COM_PowerDown( DWORD hDeviceContext )
{
	// 초기화 처리한다.
	InitData();
	IsPowerOn = TRUE;
}

/*
 * SerialPort Read 호출시 버퍼의 정보를 리턴한다.
 */
NATENMEADLL_API DWORD COM_Read( DWORD hOpenContext, LPVOID pOutBuffer, DWORD Count )
{
	DWORD	nRetCount = 0;
	IsPowerOn = FALSE;

	// 1. 버퍼의 데이터가 요청데이터보다 작은 경우 GPS에서 데이터를 더 읽어온다.
	if(nDataQueueLen < Count) {
		char	szReadBuffer[SZ_MAX_QUEUE+1];
		DWORD	dwRead = 0;

		// 변수를 초기화 한다.
		memset(szReadBuffer, 0, SZ_MAX_QUEUE+1);

		// 1-1. GPS에서 데이터를 읽어온다.
		dwRead = ReadDataFromGPS(szReadBuffer);

		// 1-2. 읽은 데이터를 변환한다.
		if(dwRead > 0)
			dwRead = ConvertNMEAData(szReadBuffer);

		// 1-3. 변환 데이터를 데이터 큐에 어펜드 한다.
		if(dwRead > 0) {
			memcpy(szDataQueue+nDataQueueLen, szReadBuffer, dwRead);
			nDataQueueLen += dwRead;
		}
	}

	// 2. 버퍼의 데이터를 요청 사이즈만큼 복사한다.
	if(nDataQueueLen <= 0)
		return 0;
	if(Count > nDataQueueLen) {
		memcpy(pOutBuffer, szDataQueue, nDataQueueLen);
		nRetCount = nDataQueueLen;
	} else {
		memcpy(pOutBuffer, szDataQueue, Count);
		nRetCount = Count;
	}

	// 3. 데이터 큐를 재조정한다.
	char	szTemp[SZ_MAX_QUEUE+1];
	memcpy(szTemp, szDataQueue+nRetCount, nDataQueueLen-nRetCount);
	nDataQueueLen -= nRetCount;
	memset(szDataQueue, 0, SZ_MAX_QUEUE+1);
	memcpy(szDataQueue, szTemp, nDataQueueLen);

	return nRetCount;
}

NATENMEADLL_API DWORD COM_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count )
{
	if (hOpenContext != OPEN_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False Write");
#endif
		return 0;
	}

#ifdef LOG_CALLS
	char buf[255];
	sprintf(buf, "Write bytes=%u", Count);
	Log(buf);
#endif
	DWORD dwBytes = 0;
	WriteFile(hComm, pBuffer, Count, &dwBytes, NULL);

	return dwBytes;
}

NATENMEADLL_API DWORD COM_Seek( DWORD hOpenContext, long Amount, WORD Type )
{
#ifdef LOG_CALLS
	char buf[255];
	sprintf(buf, "Seek Amount=%u, Type=%u", Amount, Type);
	Log(buf);
#endif

	return 0;
}
