// NateNMEADriver.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

#define LOG_DATA

DWORD dwDebugging = 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);
	}
}

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 );

#define DEVICE_CONTEXT		0x1450
#define OPEN_CONTEXT		0x1451

HANDLE hComm = INVALID_HANDLE_VALUE;
DWORD dwNateGPSPort = 1;

DWORD	flagNormalGPS = -1;
char	szBufferQueue[512];

BOOL	IsGGA = FALSE;
BOOL	IsRMC = FALSE;
int		nIndex = 0;
char	szNorth[15];
int		nNorth = 0;
char	szEast[15];
int		nEast = 0;
char	szChecksum[256];
int		nChecksumIndex = 0;

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)
{
	// 큐 버퍼를 초기화 한다.
	memset(szBufferQueue, 0, 512);

#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;

	// 변수를 초기화 처리한다.
	flagNormalGPS = -1;
	IsGGA = FALSE;
	IsRMC = FALSE;
	nIndex = 0;
	memset(szNorth, 0, 15);
	nNorth = 0;
	memset(szEast, 0, 15);
	nEast = 0;
	memset(szChecksum, 0, 256);
	nChecksumIndex = 0;	

	return DEVICE_CONTEXT;
}

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

	// 변수를 초기화 처리한다.
	flagNormalGPS = -1;
	IsGGA = FALSE;
	IsRMC = FALSE;
	nIndex = 0;
	memset(szNorth, 0, 15);
	nNorth = 0;
	memset(szEast, 0, 15);
	nEast = 0;
	memset(szChecksum, 0, 256);
	nChecksumIndex = 0;	

#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 {
		// 초기화 변수를 처리한다.
		flagNormalGPS = -1;
		IsGGA = FALSE;
		IsRMC = FALSE;
		nIndex = 0;
		memset(szNorth, 0, 15);
		nNorth = 0;
		memset(szEast, 0, 15);
		nEast = 0;
		memset(szChecksum, 0, 256);
		nChecksumIndex = 0;	
	
		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;

	// 변수를 초기화 처리한다.
	flagNormalGPS = -1;
	IsGGA = FALSE;
	IsRMC = FALSE;
	nIndex = 0;
	memset(szNorth, 0, 15);
	nNorth = 0;
	memset(szEast, 0, 15);
	nEast = 0;
	memset(szChecksum, 0, 256);
	nChecksumIndex = 0;	

	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 )
{
	// 변수를 초기화 처리한다.
	flagNormalGPS = -1;
	IsGGA = FALSE;
	IsRMC = FALSE;
	nIndex = 0;
	memset(szNorth, 0, 15);
	nNorth = 0;
	memset(szEast, 0, 15);
	nEast = 0;
	memset(szChecksum, 0, 256);
	nChecksumIndex = 0;	
}

NATENMEADLL_API void COM_PowerDown( DWORD hDeviceContext )
{
	// 변수를 초기화 처리한다.
	flagNormalGPS = -1;
	IsGGA = FALSE;
	IsRMC = FALSE;
	nIndex = 0;
	memset(szNorth, 0, 15);
	nNorth = 0;
	memset(szEast, 0, 15);
	nEast = 0;
	memset(szChecksum, 0, 256);
	nChecksumIndex = 0;	
}

//$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[10];
	char	szHigh[10], szLow[5];
	float	nfLow = 0.0;
	int		nLat = 0;
	int		temp = 0;

	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[11];
	char	szHigh[10], szLow[5];
	float	nfLow = 0.0;
	int		nLong = 0;
	int		temp = 0;

	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;
}

char *strchr_r(char *pString, char c)
{
	for(int i = strlen(pString)-1; i >= 0; i--) {
		if(*(pString+i) == c)
			return pString+i;
	}
	return NULL;
}

NATENMEADLL_API DWORD COM_Read( DWORD hOpenContext, LPVOID pOutBuffer, DWORD Count )
{
	char	readBuffer[512];
	int		bufferIndex = 0;
	int		oldGPSOffset = 0;
	int		LatOffset = 0;
	int		LongOffset = 0;
	char	*pBuffer = NULL;

	if (hOpenContext != OPEN_CONTEXT)
	{
#ifdef LOG_CALLS
		Log("False Read");
#endif
		return 0;
	}

#ifdef LOG_CALLS
	char buf[512];
	sprintf(buf, "Read max=%u", Count);
	Log(buf);
#endif

	DWORD dwBytes = 0;	
	memset(pOutBuffer, 0, Count);
	if(flagNormalGPS == 1) {	// 요청한 그대로 보내자.. (원래 GPS 방식)
		// 해당 포트에서 직접 데이터를 읽어와 리턴한다.
		ReadFile(hComm, pOutBuffer, Count, &dwBytes, NULL);

#ifdef LOG_CALLS
		char buf[512];
		char temp[256];
		memcpy(temp, pOutBuffer, dwBytes);
		temp[dwBytes] = '\0';
		sprintf(buf, "Direct Read [%d](%d) =>\n[%s]", Count, dwBytes, temp);
		Log(buf);
#endif
	} else if(strlen(szBufferQueue) >= Count) {	// 큐의 내용을 보내자...		
		// 큐의 내용을 카피
		int	nRest = strlen(szBufferQueue);
		if(nRest > Count) {	
			char	szTemp[512];
			
			strncpy((char *)pOutBuffer, szBufferQueue, Count);
			dwBytes = Count;

			// 카피된 만큼 제거후 이동한다.
			memset(szTemp, 0, 512);
			strcpy(szTemp, szBufferQueue+Count);
			memset(szBufferQueue, 0, 512);
			strcpy(szBufferQueue, szTemp);
		} else {
			strcpy((char *)pOutBuffer, szBufferQueue);
			dwBytes = nRest;

			// 버퍼를 초기화 한다.
			memset(szBufferQueue, 0, 512);
		}	
#ifdef LOG_CALLS
		char buf[512];
		char temp[256];
		memcpy(temp, pOutBuffer, dwBytes);
		temp[dwBytes] = '\0';
		sprintf(buf, " [%s][%d](%d)", temp, Count, dwBytes);
		Log(buf);
#endif
	} else {	// 포트에서 데이터를 읽어와 변환후 요청 사이즈 만큼만 넘기자..
		int		nRestOffset = strlen(szBufferQueue);
		char	*pConvPtr = szBufferQueue + nRestOffset;		
		char	*pReadBuffer = readBuffer;
		DWORD	dwTBytes = 0;
		DWORD	dwReadCount = 180 - nRestOffset - 20;
		// 통상 224바이트 정도의 데이터가 넘어온다. - 일단 라인 단위로 읽을수 있도록 한다.
		BOOL	needMore = FALSE;
		int		nLoopCount = 10;

#ifdef LOG_CALLS
		sprintf(buf, "\n\nReadData (%d) =>\n", Count);
		Log(buf);
#endif
		pBuffer = szBufferQueue + nRestOffset;
		memset(readBuffer, 0, 512);
		for(int i = 0; i < nLoopCount; i++) {	// 최대 1초 동안 루프를 돈다. 
			ReadFile(hComm, pReadBuffer, dwReadCount, &dwTBytes, NULL);
#ifdef LOG_CALLS
		sprintf(buf, "[%s](%d)", pReadBuffer, dwTBytes);
		Log(buf);
#endif
			pReadBuffer += dwTBytes;
			dwReadCount -= dwTBytes;
			dwBytes += dwTBytes;
			char *pRev = strchr_r(readBuffer, '$');
			if(pRev != NULL) {
				char *ptr1 = strstr(pRev, "$GPGGA");
				char *ptr2 = strstr(pRev, "$GPRMC");
				int	nTailLen = strlen(pRev);
				if(ptr1 != NULL) {
					char *ptr = strchr(ptr1, '*');
					if(ptr != NULL && strlen(ptr) >= 3 && nTailLen >= 6)
						break;
					// 버퍼가 적으면 좀더 읽어들인다.
					if(dwReadCount < 80)
						dwReadCount += 80;
					needMore = TRUE;
				} else if(ptr2 != NULL) {
					char *ptr = strchr(ptr2, '*');
					if(ptr != NULL && strlen(ptr) >= 3 && nTailLen >= 6)
						break;
					// 버퍼가 적으면 좀더 읽어들인다.
					if(dwReadCount < 80)
						dwReadCount += 80;
					needMore = TRUE;
				}
				// 끝부분에 $가 나오고 거의 버퍼가 끝나면 6개를 더읽어 헤더를 정확히 읽는다.			
				if(nTailLen < 6 && dwReadCount < 6)
					dwReadCount += 6;
			}
			if(dwReadCount <= 0)
				break;
			if(*(pReadBuffer-1) == 0x0A)
				break;
			if(i+1 == nLoopCount && needMore)
				nLoopCount++;
			Sleep(80);			
		}

#ifdef LOG_DATA
		char buf[512];
		sprintf(buf, "Read Data (%d+%d) =>\n[%s]", strlen(szBufferQueue), dwBytes, readBuffer);
		Log(buf);
#endif

		// 1. $GPGGA 혹은 $GPRMC 을 추출한다.
		// 변수들을 초기화 한다.
		if(nNorth == 0)
			memset(szNorth, 0, 15);
		if(nEast == 0) 
			memset(szEast, 0, 15);
		if(nChecksumIndex == 0)
			memset(szChecksum, 0, 256);
		char	*pBufferPtr = readBuffer;

		// 앞에 연결된 버퍼에서 데이터 추출한다.
		if(nIndex > 0 && *pBufferPtr != '$' && (IsGGA || IsRMC)) {
			int	nRest = strlen(pBufferPtr);

			if(IsGGA) {
				oldGPSOffset = 21;
				LatOffset = 17;
				LongOffset = 27;
			} else if(IsRMC) {
				oldGPSOffset = 23;
				LatOffset = 19;
				LongOffset = 29;
			} 

			for(int i = 0; i < nRest; i++) {
				char	c = *(pBufferPtr+i);

				// 정상적인 GPS 정보 확인
				if(nIndex == oldGPSOffset && c == '.') {
					nChecksumIndex = 0;
					nIndex = 0;
					nNorth = 0;
					nEast = 0;
					flagNormalGPS = 1;
					strcpy((char *)pBuffer, readBuffer);
					break;
				}

				if(nIndex >= LatOffset && nIndex <= LatOffset+6) {	// 위도 
					szNorth[nNorth++] = c;
					if(nIndex == LatOffset+6) {
						// 위도 계산
						strcpy(szNorth, CalcLatitude(szNorth));
						strcat(szChecksum, szNorth);
						strcpy((char *)pBuffer+bufferIndex-6, szNorth);
						nChecksumIndex += 9;
						nNorth = 0;
						bufferIndex += 2;
						dwBytes += 2;
						memset(szNorth, 0, 15);
					}
				} else if(nIndex >= LongOffset && nIndex <= LongOffset+6) {	// 경도
					szEast[nEast++] = c;
					if(nIndex == LongOffset+6) {
						// 경도 계산
						strcpy(szEast, CalcLongitude(szEast));
						strcat(szChecksum, szEast);
						strcpy((char *)pBuffer+bufferIndex-6, szEast);
						nChecksumIndex += 10;
						nEast = 0;
						bufferIndex += 3;
						dwBytes += 3;
						memset(szEast, 0, 15);
					}
				} else if(c != '$' && c != '*' && c != '\n' && c != '\r') {
					szChecksum[nChecksumIndex++] = c;
					*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
				} else if(c == '*') {
					// 체크섬 대체					
					*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
					strcpy((char *)pBuffer+bufferIndex+1, CalcChecksum(szChecksum));
					i += 2;
					bufferIndex += 2;
					nChecksumIndex = 0;
					memset(szChecksum, 0, 256);
					IsGGA = FALSE;
					IsRMC = FALSE;
				} else if(c == '$') {
					nIndex = 0;
					nChecksumIndex = 0;
					pBufferPtr = pBufferPtr+i;
					break;
				} else {
					*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
				}
				bufferIndex++;
				nIndex++;
				if(i+1 == nRest)
					pBufferPtr += nRest;
			}
		}

		// 다시 버퍼를 검색하여 데이터 추출한다.
		while(pBufferPtr != NULL && *pBufferPtr != '\0') {
			char	*ptr = strchr(pBufferPtr, '$');
			if(nIndex == 0) {
				if(pBufferPtr != ptr && ptr != NULL) {
					strncpy((char *)pBuffer+strlen((char *)pBuffer), pBufferPtr, ptr - pBufferPtr);
					bufferIndex += strlen((char *)pBuffer);
				}
				if(ptr != NULL)
					pBufferPtr = ptr;
			}
			if(ptr != NULL) {
				if(strncmp(pBufferPtr, "$GPGGA", 6) == 0 || strncmp(pBufferPtr, "$GPRMC", 6) == 0) {
					if(strncmp(pBufferPtr, "$GPGGA", 6) == 0) {
						IsGGA = TRUE;
						oldGPSOffset = 21;
						LatOffset = 17;
						LongOffset = 27;
					} else {
						IsRMC = TRUE;
						oldGPSOffset = 23;
						LatOffset = 19;
						LongOffset = 29;
					}
					
					nIndex = 0;
					pBufferPtr++;
					int	nRest = strlen(pBufferPtr);
					strcat((char *)pBuffer, "$");
					bufferIndex++;
					for(int i = 0; i < nRest; i++) {
						char	c = *(pBufferPtr+i);

						// 정상적인 GPS 정보 확인
						if(i == oldGPSOffset && c == '.') {
							nChecksumIndex = 0;
							nIndex = 0;
							nNorth = 0;
							nEast = 0;
							flagNormalGPS = 1;
							strcpy((char *)pBuffer, readBuffer);
							break;
						}

						if(i >= LatOffset && i <= LatOffset+6) {	// 위도 
							szNorth[nNorth++] = c;
							if(i == LatOffset+6) {
								// 위도 계산
								strcpy(szNorth, CalcLatitude(szNorth));
								strcat(szChecksum, szNorth);
								strcpy((char *)pBuffer+bufferIndex-6, szNorth);
								nChecksumIndex += 9;
								nNorth = 0;
								bufferIndex += 2;
								dwBytes += 2;
								memset(szNorth, 0, 15);
							}
						} else if(i >= LongOffset && i <= LongOffset+6) {	// 경도
							szEast[nEast++] = c;
							if(i == LongOffset+6) {
								// 경도 계산
								strcpy(szEast, CalcLongitude(szEast));
								strcat(szChecksum, szEast);
								strcpy((char *)pBuffer+bufferIndex-6, szEast);
								nChecksumIndex += 10;
								nEast = 0;
								bufferIndex += 3;
								dwBytes += 3;
								memset(szEast, 0, 15);
							}
						} else if(c != '$' && c != '*' && c != '\n' && c != '\r') {
							szChecksum[nChecksumIndex++] = c;
							*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
						} else if(c == '*') {
							// 체크섬 대체
							*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
							strcpy((char *)pBuffer+bufferIndex+1, CalcChecksum(szChecksum));
							i += 2;
							bufferIndex += 2;
							nChecksumIndex = 0;
							memset(szChecksum, 0, 256);
							IsGGA = FALSE;
							IsRMC = FALSE;
						} else if(c == '$') {
							// 다음부터 수행.
							nIndex = 0;
							nChecksumIndex = 0;
							pBufferPtr = pBufferPtr+i;
							break;
						} else {
							*((char *)pBuffer+bufferIndex) = *(pBufferPtr+i);
						}
						bufferIndex++;
						nIndex++;
						if(i+1 == nRest)
							pBufferPtr += nRest;
					}
				} else {
					strcpy((char *)pBuffer+bufferIndex, pBufferPtr);
					pBufferPtr++;
					ptr = strchr(pBufferPtr, '$');
					if(ptr != NULL) {
						*((char *)pBuffer+bufferIndex+(ptr-pBufferPtr)+1) = '\0';
						bufferIndex += ptr - pBufferPtr;
						bufferIndex++;
						pBufferPtr = ptr;
					} else {
						pBufferPtr = pBufferPtr+strlen(pBufferPtr);
					}
					nIndex = 0;
				}
			} else {
				strcpy((char *)pBuffer+bufferIndex, pBufferPtr);
				pBufferPtr++;
				ptr = strchr(pBufferPtr, '$');
				if(ptr != NULL) {
					*((char *)pBuffer+bufferIndex+(ptr-pBufferPtr)+1) = '\0';
					bufferIndex += ptr - pBufferPtr;
					bufferIndex++;
					pBufferPtr = ptr;
				} else {
					pBufferPtr = pBufferPtr+strlen(pBufferPtr);
				}
				nIndex = 0;
			}
		}
		//*((char *)pBuffer+dwBytes) = '\0';
		dwBytes += nRestOffset;
		szBufferQueue[dwBytes] = '\0';

#ifdef LOG_DATA
		sprintf(buf, "Convert Data (%d)(%d) =>\n[%s]", nRestOffset, dwBytes, pConvPtr);
		Log(buf);
#endif

		// 요청한 만큼만 전송한다.		
		if(dwBytes > Count) {
			char	szTemp[512];

			// 버퍼를 복사한다.
			strncpy((char *)pOutBuffer, szBufferQueue, Count);
			dwBytes = Count;

			// 복사된 만큼 제거후 이동한다.
			strcpy(szTemp, szBufferQueue+Count);
			memset(szBufferQueue, 0, 512);
			strcpy(szBufferQueue, szTemp);
		} else {
			// 버퍼를 복사한다.
			strcpy((char *)pOutBuffer, szBufferQueue);

			// 버퍼를 초기화 한다.
			memset(szBufferQueue, 0, 512);
		}
#ifdef LOG_CALLS
		char temp[256];
		memcpy(temp, pOutBuffer, dwBytes);
		temp[dwBytes] = '\0';
		sprintf(buf, " [%s][%d](%d)", temp, Count, dwBytes);
		Log(buf);
#endif
	}

	return dwBytes;
}

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;
}
