scbc.sat2 5 роки тому
батько
коміт
496c863fa3

+ 26 - 0
ScbcCopyKey/ScbcCopyKey.sln

@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScbcCopyKey", "ScbcCopyKey\ScbcCopyKey.vcproj", "{AC652CEB-5047-4783-AA18-6B64AA4E78B5}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		DebugEXE|Win32 = DebugEXE|Win32
+		Release|Win32 = Release|Win32
+		ReleasePYD|Win32 = ReleasePYD|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.Debug|Win32.ActiveCfg = Debug|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.Debug|Win32.Build.0 = Debug|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.DebugEXE|Win32.ActiveCfg = DebugEXE|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.DebugEXE|Win32.Build.0 = DebugEXE|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.Release|Win32.ActiveCfg = Release|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.Release|Win32.Build.0 = Release|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.ReleasePYD|Win32.ActiveCfg = ReleasePYD|Win32
+		{AC652CEB-5047-4783-AA18-6B64AA4E78B5}.ReleasePYD|Win32.Build.0 = ReleasePYD|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 84 - 0
ScbcCopyKey/ScbcCopyKey/CritSection.h

@@ -0,0 +1,84 @@
+#ifndef __CRITSECTION_20160221__
+#define __CRITSECTION_20160221__
+
+// ÁÙ½çÖµ;
+class ThreadSection
+{
+public:
+	ThreadSection(){
+		HRESULT hr = Init();
+		(hr);
+	}
+
+	~ThreadSection(){
+		DeleteCriticalSection(&_CriticalSection);
+	}
+
+	bool Lock()
+	{
+		bool result = false;
+		__try
+		{
+			EnterCriticalSection(&_CriticalSection);
+			result = true;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+		}
+		return result;
+	}
+
+	bool Unlock()
+	{
+		bool result = false;
+		__try
+		{
+			LeaveCriticalSection(&_CriticalSection);
+			result = true;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+		}
+		return result;
+	}
+
+private:
+	HRESULT Init() throw()
+	{
+		HRESULT hRes = E_FAIL;
+		__try
+		{
+			InitializeCriticalSection(&_CriticalSection);
+			hRes = S_OK;
+		}
+		__except (STATUS_NO_MEMORY == GetExceptionCode())
+		{
+			hRes = E_OUTOFMEMORY;
+		}
+		return hRes;
+	}
+
+	ThreadSection(const ThreadSection & tSection);
+	ThreadSection &operator=(const ThreadSection & tSection);
+	CRITICAL_SECTION _CriticalSection;
+};
+
+
+class AutoThreadSection
+{
+public:
+	AutoThreadSection(IN ThreadSection* pSection){
+		_pSection = pSection;
+		_pSection->Lock();
+	}
+
+	~AutoThreadSection(){
+		_pSection->Unlock();
+	}
+private:
+	AutoThreadSection(const AutoThreadSection & tSection);
+	AutoThreadSection &operator=(const AutoThreadSection & tSection);
+	ThreadSection * _pSection;
+};
+
+#endif //__CRITSECTION_20160221__

+ 483 - 0
ScbcCopyKey/ScbcCopyKey/Global.cpp

@@ -0,0 +1,483 @@
+#include "stdafx.h"
+#include "Global.h"
+#include <SetupAPI.h>
+#include <InitGuid.h>
+#include <WinIoCtl.h>
+#pragma comment(lib, "SetupAPI.lib")
+
+namespace Global
+{
+	//////////////////////////////////////////////////////////////////////////
+	// 全局变量;
+	BOOL g_bTestHost = FALSE;
+	TCHAR g_szCurModuleDir[MAX_PATH] = { 0 };
+	TCHAR g_szCurModulePath[MAX_PATH] = { 0 };
+	TCHAR g_szFna[MAX_PATH] = { 0 };
+	TCHAR g_szConfig[MAX_PATH] = { 0 };
+	std::string g_strMacs;
+	std::vector<MacAddress> g_vtMac;
+
+	// usb;
+	static GUID UsbClassGuid = { 0xA5DCBF10L, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} };
+	// mac;
+	static GUID MacClassGuid = { 0xAD498944, 0x762F, 0x11D0, {0x8D, 0xCB, 0x00, 0xC0, 0x4F, 0xC3, 0x35, 0x8C} };
+	// hdd;
+	static GUID HDDClassGuid = { 0x53F56307, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B} };
+
+	//////////////////////////////////////////////////////////////////////////
+	// 全局函数;
+	/************************************************************************/
+	/*  函数:WriteTextLog[7/28/2009 Jeff];
+			/*  描述:写文本日志;
+			/*  参数:;
+			/*  	[IN] :;
+			/*  返回:void;
+			/*  注意:;
+			/*  示例:;
+			/*
+			/*  修改:;
+			/*  日期:;
+			/*  内容:;
+			/************************************************************************/
+	void WriteTextLog(const TCHAR* format, ...)
+	{
+		// 将日志内容输入到文件中;
+		// 获取今年年份;
+		__time64_t gmt = time(NULL); // 获取当前日历时间(1900-01-01开始的Unix时间戳);
+		struct tm gmtm = { 0 };
+		localtime_s(&gmtm, &gmt); // 时间戳转成本地时间;
+
+		// 解析出日志路径;
+		TCHAR szlogpath[MAX_PATH] = { 0 };
+		_stprintf_s(szlogpath, _T("%slog\\Serial Port%s Log %02d%02d.txt"), g_szCurModuleDir, g_szFna, gmtm.tm_mon + 1, gmtm.tm_mday);
+		// 打开或创建文件;
+		FILE* fp = NULL;
+		//if (_taccess(szlogpath, 0) != -1)
+#ifndef UNICODE
+		if (_access(szlogpath, 0) != -1)
+#else
+		if (_taccess(szlogpath, 0) != -1)
+#endif
+		{ // 存在;
+			if (0 == _tfopen_s(&fp, szlogpath, _T("a+")))
+				// 移动到末尾;
+				fseek(fp, 0, SEEK_END);
+		}
+		else
+		{ // 不存在;
+			_tfopen_s(&fp, szlogpath, _T("w+"));
+		}
+
+		if (fp == NULL)
+			return;
+
+		// 格式化前设置语言区域;
+		TCHAR* old_locale = _tcsdup(_tsetlocale(LC_CTYPE, NULL));
+		_tsetlocale(LC_CTYPE, _T("chs")); //设定中文;
+
+		// 格式化日志内容;
+		va_list args = NULL;
+		int len = 0;
+		TCHAR* buffer = NULL;
+		va_start(args, format);
+		// _vscprintf doesn't count. terminating '\0'
+		len = _vsctprintf(format, args) + 1;
+		buffer = (TCHAR*)malloc(len * sizeof(TCHAR));
+		_vstprintf_s(buffer, len, format, args);
+		_ftprintf(fp, _T("%04d-%02d-%02d %02d:%02d:%02d %s\n"), gmtm.tm_year + 1990, gmtm.tm_mon + 1, gmtm.tm_mday, gmtm.tm_hour, gmtm.tm_min, gmtm.tm_sec, buffer);
+
+		// 关闭文件,释放资源并设置回原语言区域;
+		free(buffer);
+		fclose(fp);
+		_tsetlocale(LC_CTYPE, old_locale);
+		free(old_locale); //还原区域设定;
+	}
+
+
+	void replace_str(std::string& str, const std::string& before, const std::string& after)
+	{
+		for (std::string::size_type pos(0); pos != std::string::npos; pos += after.length())
+		{
+			pos = str.find(before, pos);
+			if (pos != std::string::npos)
+				str.replace(pos, before.length(), after);
+			else
+				break;
+		}
+	}
+
+	// 去除空格;
+	std::string& trim(std::string& str)
+	{
+		int nIndex = 0;
+		while ((nIndex = str.find_first_of(' ')) != std::string::npos)
+			str.erase(nIndex, 1);
+
+		return str;
+	}
+
+	// hModule 模块句柄 NULL表示当前模块;
+	bool GetVersion(IN const TCHAR* fname, OUT WORD* pdwFileVersion, OUT WORD* pdwProductVerion)
+	{
+		VS_FIXEDFILEINFO* pVi = NULL;
+		DWORD dwHandle = 0;
+		int size = GetFileVersionInfoSize(fname, &dwHandle);
+		if (size > 0)
+		{
+			BYTE* buffer = new BYTE[size];
+			memset(buffer, 0, size);
+
+			if (GetFileVersionInfo(fname, 0, size, buffer))
+			{
+				if (VerQueryValue(buffer, _T("\\"), (LPVOID*)& pVi, (PUINT)& size))
+				{
+					pdwFileVersion[0] = HIWORD(pVi->dwFileVersionMS);
+					pdwFileVersion[1] = LOWORD(pVi->dwFileVersionMS);
+					pdwFileVersion[2] = HIWORD(pVi->dwFileVersionLS);
+					pdwFileVersion[3] = LOWORD(pVi->dwFileVersionLS);
+
+					pdwProductVerion[0] = HIWORD(pVi->dwProductVersionMS);
+					pdwProductVerion[1] = LOWORD(pVi->dwProductVersionMS);
+					pdwProductVerion[2] = HIWORD(pVi->dwProductVersionLS);
+					pdwProductVerion[3] = LOWORD(pVi->dwProductVersionLS);
+
+					delete[] buffer;
+					return true;
+				}
+			}
+
+			delete[] buffer;
+		}
+
+		return false;
+	}
+
+	BOOL GetVersion(IN HMODULE hModule, OUT DWORD(&dwFVArray)[4], OUT DWORD(&dwPVArray)[4])
+	{
+		TCHAR fname[MAX_PATH];
+		VS_FIXEDFILEINFO* pVi;
+		DWORD dwHandle;
+
+		if (GetModuleFileName(hModule, fname, MAX_PATH))
+		{
+			INT nSize = GetFileVersionInfoSize(fname, &dwHandle);
+
+			if (nSize > 0)
+			{
+				BYTE* pBuffer = new BYTE[nSize];
+				memset(pBuffer, 0, nSize);
+
+				if (GetFileVersionInfo(fname, 0, nSize, pBuffer))
+				{
+					if (VerQueryValue(pBuffer, _T("\\"), (LPVOID*)& pVi, (PUINT)& nSize))
+					{
+						dwFVArray[0] = HIWORD(pVi->dwFileVersionMS);
+						dwFVArray[1] = LOWORD(pVi->dwFileVersionMS);
+						dwFVArray[2] = HIWORD(pVi->dwFileVersionLS);
+						dwFVArray[3] = LOWORD(pVi->dwFileVersionLS);
+
+						dwPVArray[0] = HIWORD(pVi->dwProductVersionMS);
+						dwPVArray[1] = LOWORD(pVi->dwProductVersionMS);
+						dwPVArray[2] = HIWORD(pVi->dwProductVersionLS);
+						dwPVArray[3] = LOWORD(pVi->dwProductVersionLS);
+
+						delete[]pBuffer;
+						return TRUE;
+					}
+				}
+
+				if (pBuffer)
+					delete[]pBuffer;
+			}
+		}
+
+		return FALSE;
+	}
+
+	WCHAR* ASCII2UNICODE(IN LPCCH lpASCIIStr)
+	{
+		if (lpASCIIStr == NULL)
+			return NULL;
+
+		// 获取宽字符字节数;
+		int cchWideChar = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, NULL, 0);
+		if (cchWideChar == 0)
+			return NULL;
+
+		// 转换成宽字符串;
+		WCHAR* pWideChar = new WCHAR[cchWideChar + 1];
+		memset(pWideChar, 0, sizeof(WCHAR) * (cchWideChar + 1));
+		int nWriteNum = MultiByteToWideChar(CP_ACP, 0, lpASCIIStr, -1, pWideChar, cchWideChar);
+		if (nWriteNum != cchWideChar)
+		{
+			if (pWideChar)
+				delete[] pWideChar;
+			return NULL;
+		}
+
+		return pWideChar;
+	}
+
+	BOOL UNICODE2UTF8(IN LPWCH lpUNICODEStr, OUT std::string& strResult)
+	{
+		if (lpUNICODEStr == NULL)
+			return FALSE;
+
+		// 获取多字节字符字节数;
+		int cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, lpUNICODEStr, -1, NULL, 0, NULL, NULL);
+		if (cbMultiByte == 0)
+			return FALSE;
+
+		// 转换成多字节字符;
+		CHAR* pResult = new CHAR[cbMultiByte];
+		memset(pResult, 0, cbMultiByte);
+		int nWriteNum = WideCharToMultiByte(CP_UTF8, 0, lpUNICODEStr, -1, pResult, cbMultiByte, NULL, NULL);
+		if (nWriteNum != cbMultiByte)
+			return FALSE;
+
+		strResult = pResult;
+		if (pResult)
+			delete[] pResult;
+
+		return TRUE;
+	}
+
+	BOOL ASCII2UTF8(IN LPCCH lpASCIIStr, OUT std::string& strResult)
+	{
+		// 将ASCII字符串转成UNICODE字符串;
+		WCHAR* pWideChar = ASCII2UNICODE(lpASCIIStr);
+		if (pWideChar == NULL)
+			return FALSE;
+
+		// 再将UICODE转成UTF8;
+		BOOL bResult = UNICODE2UTF8(pWideChar, strResult);
+
+		if (pWideChar)
+			delete[] pWideChar;
+
+		return bResult;
+	}
+
+	std::string EnCode_UTF8URL(IN const CHAR* pText)
+	{
+		std::string tt = "";
+		std::string dd = "";
+		ASCII2UTF8(pText, tt);
+
+		size_t len = tt.length();
+		for (size_t i = 0; i < len; i++)
+		{
+			if (isalnum((BYTE)tt.at(i)))
+			{
+				char tempbuff[2] = { 0 };
+				sprintf_s(tempbuff, "%c", (BYTE)tt.at(i));
+				dd.append(tempbuff);
+			}
+			else if (isspace((BYTE)tt.at(i)))
+			{
+				dd.append("+");
+			}
+			else
+			{
+				char tempbuff[4];
+				sprintf_s(tempbuff, "%%%X%X", ((BYTE)tt.at(i)) >> 4, ((BYTE)tt.at(i)) % 16);
+				dd.append(tempbuff);
+			}
+		}
+		return dd;
+	}
+
+	void EnCode_UTF8URL(IN const CHAR* pText, OUT std::string& strResult)
+	{
+		std::string tt = "";
+		ASCII2UTF8(pText, tt);
+
+		size_t len = tt.length();
+		for (size_t i = 0; i < len; i++)
+		{
+			if (isalnum((BYTE)tt.at(i)))
+			{
+				char tempbuff[2] = { 0 };
+				sprintf_s(tempbuff, "%c", (BYTE)tt.at(i));
+				strResult.append(tempbuff);
+			}
+			else if (isspace((BYTE)tt.at(i)))
+			{
+				strResult.append("+");
+			}
+			else
+			{
+				char tempbuff[4];
+				sprintf_s(tempbuff, "%%%X%X", ((BYTE)tt.at(i)) >> 4, ((BYTE)tt.at(i)) % 16);
+				strResult.append(tempbuff);
+			}
+		}
+	}
+
+	// 通过注册表查找系统当前串口信息;
+	BOOL GetSysSerialPort(std::vector<std::string>& vtports)
+	{
+		HKEY hKey;
+		LSTATUS lReg = 0;
+		DWORD dwMaxValLen = 0;
+		DWORD dwValNum = 0;
+		lReg = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey);
+		if (lReg != ERROR_SUCCESS)
+		{
+			return FALSE;
+		}
+
+		lReg = RegQueryInfoKeyA(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValNum, &dwMaxValLen, NULL, NULL, NULL);
+		if (lReg != ERROR_SUCCESS)
+		{
+			return FALSE;
+		}
+
+		if (vtports.size())
+		{
+			vtports.clear();
+		}
+
+		LPSTR lpValName, lpComNum;
+		DWORD dwValName, dwValSize = 6;
+		for (DWORD i = 0; i < dwValNum; i++)
+		{
+			dwValName = dwMaxValLen + 1;
+			dwValSize = 6;
+			lpValName = (LPSTR)VirtualAlloc(NULL, dwValName, MEM_COMMIT, PAGE_READWRITE);
+			lReg = RegEnumValueA(hKey, i, lpValName, &dwValName, NULL, NULL, NULL, NULL);
+			if ((lReg != ERROR_SUCCESS) && (lReg != ERROR_NO_MORE_ITEMS))
+			{
+				continue;
+			}
+			lpComNum = (LPSTR)VirtualAlloc(NULL, 6, MEM_COMMIT, PAGE_READWRITE);
+			lReg = RegQueryValueExA(hKey, lpValName, NULL, NULL, (LPBYTE)lpComNum, &dwValSize);
+			if (lReg != ERROR_SUCCESS)
+			{
+				continue;
+			}
+
+			vtports.push_back(lpComNum);
+			VirtualFree(lpValName, 0, MEM_RELEASE);
+			VirtualFree(lpComNum, 0, MEM_RELEASE);
+		}
+
+		return TRUE;
+	}
+
+	INT GetMacAddress()
+	{
+		HDEVINFO hDevInfo;
+		DWORD MemberIndex, RequiredSize;
+		SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
+		PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
+		INT nTotal = 0;
+		INT nNICKind = 0;
+
+		// 获取设备信息集;
+		hDevInfo = SetupDiGetClassDevs(&MacClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
+		if (hDevInfo == INVALID_HANDLE_VALUE)
+		{
+			return -1;
+		}
+
+		g_vtMac.clear();
+		// 枚举设备信息集中所有设备;
+		DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+		for (MemberIndex = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &MacClassGuid, MemberIndex, &DeviceInterfaceData); MemberIndex++)
+		{
+			// 获取接收缓冲区大小,函数返回值为FALSE,GetLastError()=ERROR_INSUFFICIENT_BUFFER;
+			SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, NULL, 0, &RequiredSize, NULL);
+
+			// 申请接收缓冲区;
+			DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);
+			if ( DeviceInterfaceDetailData == NULL)
+				continue;
+			DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+			// 获取设备细节信息;
+			if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, DeviceInterfaceDetailData, RequiredSize, NULL, NULL))
+			{
+				HANDLE hDeviceFile;
+				BOOL isOK = FALSE;
+				if (_tcsnicmp(DeviceInterfaceDetailData->DevicePath + 4, TEXT("pci"), 3) != 0)
+				{
+					free(DeviceInterfaceDetailData);
+					DeviceInterfaceDetailData = NULL;
+					continue;
+				}
+				MacAddress tagMacAddress;
+				tagMacAddress.nNICKind = NIC_PCI;
+				_stprintf_s(tagMacAddress.szNICKind, _T("%s"), _T("NIC_PCI"));
+
+				// 获取设备句柄;
+				hDeviceFile = CreateFile(DeviceInterfaceDetailData->DevicePath,
+					0,
+					FILE_SHARE_READ | FILE_SHARE_WRITE,
+					NULL,
+					OPEN_EXISTING,
+					0,
+					NULL);
+
+				if (hDeviceFile != INVALID_HANDLE_VALUE)
+				{
+					ULONG dwID;
+					BYTE ucData[8];
+					DWORD dwByteRet;
+
+					// 获取原生MAC地址;
+					dwID = OID_802_3_PERMANENT_ADDRESS;
+					isOK = DeviceIoControl(hDeviceFile, IOCTL_NDIS_QUERY_GLOBAL_STATS, &dwID, sizeof(dwID), ucData, sizeof(ucData), &dwByteRet, NULL);
+					if (isOK)
+					{
+						++nTotal;
+						_stprintf_s(tagMacAddress.szDevicePath, _T("%s"), DeviceInterfaceDetailData->DevicePath);
+						memset(tagMacAddress.szMacAddress, 0, sizeof(TCHAR) * MAX_PATH);
+						// 将字节数组转换成16进制字符串;
+						for (DWORD i = 0; i < dwByteRet; i++)
+						{
+							_stprintf_s(&tagMacAddress.szMacAddress[i * 3], MAX_PATH - (i * 3), (i != dwByteRet - 1) ? _T("%02X-") : _T("%02X"), ucData[i]);
+						}
+
+						g_vtMac.push_back(tagMacAddress);
+					}
+					CloseHandle(hDeviceFile);
+				}
+			}
+
+			free(DeviceInterfaceDetailData);
+			DeviceInterfaceDetailData = NULL;
+		}
+
+		SetupDiDestroyDeviceInfoList(hDevInfo);
+
+#if 1
+		g_strMacs.clear();
+		std::vector<MacAddress>::iterator it = g_vtMac.begin();
+		for (; it != g_vtMac.end(); it++)
+		{
+			g_strMacs.append(it->szMacAddress);
+			g_strMacs.append("&");
+		}
+#endif
+
+		return nTotal;
+	}
+
+	BOOL IsValidString(LPCTSTR lpszString)
+	{
+		if (lpszString == NULL)
+			return FALSE;
+
+		do
+		{
+			// ASCII可显示的字符;
+			if (*lpszString < 32 || *lpszString > 126)
+			{
+				return FALSE;
+			}
+		} while (*++lpszString);
+
+		return TRUE;
+	}
+} // namespace Global

+ 62 - 0
ScbcCopyKey/ScbcCopyKey/Global.h

@@ -0,0 +1,62 @@
+
+#pragma once
+#include <stdio.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <time.h> //或者 #include <ctime>
+#include <io.h>
+#pragma comment(lib, "version.lib")
+#include "tinyxml2.h"
+#include <vector>
+#include <string>
+
+namespace Global
+{
+	// 网卡类型;
+	enum NICKIND
+	{
+		// pci网卡;
+		NIC_PCI,
+		// usb网卡;
+		NIC_USB,
+		// 虚拟网卡;
+		NIC_ROOT,
+		// 仿真网卡;
+		NIC_SWD,
+		// 未知类型;
+		NIC_UNK
+	};
+
+	typedef struct __MAC_ADDRESS__
+	{
+		INT nNICKind;
+		TCHAR szNICKind[16];
+		TCHAR szMacAddress[MAX_PATH];
+		TCHAR szDevicePath[MAX_PATH];
+	} MacAddress, * pMacAddress;
+	//////////////////////////////////////////////////////////////////////////
+	// 全局变量;
+	extern BOOL g_bTestHost;
+	extern TCHAR g_szCurModuleDir[MAX_PATH];
+	extern TCHAR g_szCurModulePath[MAX_PATH];
+	extern TCHAR g_szFna[MAX_PATH];
+	extern TCHAR g_szConfig[MAX_PATH];
+	extern std::string g_strMacs;
+	extern std::vector<MacAddress> g_vtMac;
+
+	//////////////////////////////////////////////////////////////////////////
+	// 全局函数;
+	bool GetVersion(IN const TCHAR* fname, OUT WORD* pdwFileVersion, OUT WORD* pdwProductVerion);
+	BOOL GetVersion(IN HMODULE hModule, OUT DWORD(&dwFVArray)[4], OUT DWORD(&dwPVArray)[4]);
+	void WriteTextLog(const TCHAR* format, ...);
+
+	std::string EnCode_UTF8URL(IN const CHAR* pText);
+	std::string& trim(std::string& str);
+	// 获取网卡地址;
+	INT GetMacAddress();
+	// 字符串是否由数字、字母、符号组成;
+	BOOL IsValidString(LPCTSTR lpszString);
+
+	// 获取系统当前串口数量;
+	BOOL GetSysSerialPort(std::vector<std::string>& vtports);
+}; // namespace Global

+ 1596 - 0
ScbcCopyKey/ScbcCopyKey/OTA.cpp

@@ -0,0 +1,1596 @@
+#include "stdafx.h"
+#include "OTA.h"
+
+ThreadSection g_csTask;
+static _SIACP_ g_Siacp[42] = {
+	{"EnterFactory", "AA", "10", "01", false, 100, 100},
+	{"LeaveFactory", "AA", "10", "00", false, 100, 100},
+	{"WBInit", "AA", "16", "02", false, 100, 100},//白平衡数据初始化(适用于所有信源)
+	{"GetProjectID", "AA", "84", "00", false, 100, 100},
+	{"GetSoftVersion", "AA", "57", "00", false, 100, 100},
+	{"GetDeviceId", "AA", "BE", "01", true, 100, 100},
+	{"GetClientType", "AA", "8C", "00", false, 100, 100},
+	{"GetMAC", "AA", "BE", "00", true, 100, 100},
+	{"GetHDCPKey", "AA", "EE", "00", false, 100, 100},
+	{"GetHDCPKey22", "AA", "E7", "00", false, 100, 100},
+	{"GetWidi", "AA", "E7", "00", false, 100, 100},
+	{"GetNetflixESN", "AA", "BE", "06", true, 100, 100},
+	{"GetWidevine", "AA", "EC", "00", true, 500,500},
+	{"GetCiKey", "AA", "EC", "01", true, 500,500},
+	{"GetOSDLanguage", "AA", "97", "00", true, 100, 100},
+	{"GetShopLanguage", "AA", "97", "01", true, 100, 100},
+	{"GetChannel", "AA", "97", "13", true, 100, 100},
+	// 设置;
+	{"SetProjectId", "AA", "70", "", false, 100, 100},
+	{"SetDeviceID", "AA", "B2", "", false, 100, 100},
+	{"SetMAC", "AA", "B3", "", false, 100, 100},
+	{"SetHDCPKey", "AA FE", "86", "", false, 100, 100},
+	{"SetHDCPKey22", "AA FE", "E4", "", false, 100, 100},
+	{"SetNetflixESN", "AA", "99 00", "", false, 100, 100},
+	{"SetWidi", "AA FE", "E4", "", false, 100, 100}, //与hdcp22相同指令
+	{"SetWidevine", "AA FE", "E9 00", "", false, 500,500},
+	{"SetCiKey", "AA FE", "E9 01", "", false, 500,500},
+	{"SetOSDLanguage", "AA", "96 00", "", true, 100, 100},
+	{"SetShopLanguage", "AA", "96 01", "", true, 100, 100},
+	// 设置;
+	{"SetChannel", "AA", "15", "", false, 100, 100},
+	{"SetWBNormal", "AA", "4D 04 01 01", "", false, 100, 100},
+	{"SetWBCool", "AA", "4D 04 02 01", "", false, 100, 100},
+	{"SetWBWarm", "AA", "4D 04 03 01", "", false, 100, 100},
+	// 检测;
+	{"CheckMAC", "AA", "B4", "00", false, 100, 100},
+	{"CheckDeviceId", "AA", "B4", "00", false, 100, 100},
+	{"CheckHDCP", "AA", "87", "00", false, 100, 100},
+	{"CheckNetflixESN", "AA", "9A", "00", true, 100, 100},
+	{"CheckWidi", "AA", "E5", "00", false, 100, 100},
+	{"CheckWidevine", "AA", "EA", "00", true, 100, 100},
+
+	{"CheckCikey", "AA", "EA", "01", true, 100, 100},
+	{"CheckHDCP22", "AA", "E5", "00", false, 100, 100},
+	{"StarWarmUpMode", "AA", "13", "01", false, 100, 100},
+	{"StopWarmUpMode", "AA", "13", "00", false, 100, 100}
+};
+
+std::vector<_SIACP_> g_vtSiacp;
+
+const unsigned short CRC16_TABLE[16] = {
+	0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+	0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF
+};
+
+unsigned short CRC16Calculate(byte *pBuffer, unsigned int wordLength)
+{
+	unsigned short wordCRC;
+	byte byteTemp;
+
+	wordCRC = 0xFFFF;
+	while (wordLength--)
+	{
+		byteTemp = (byte)(wordCRC >> 0x0C);
+		wordCRC <<= 4;
+		wordCRC ^= CRC16_TABLE[byteTemp ^ ((*pBuffer) >> 0x04)];
+		byteTemp = (byte)(wordCRC >> 0x0C);
+		wordCRC <<= 4;
+		wordCRC ^= CRC16_TABLE[byteTemp ^ ((*pBuffer) & 0x0F)];
+		pBuffer++;
+	}
+
+	return wordCRC;
+}
+
+unsigned char TwoHexChar2Char(char ch1, char ch2)
+{
+	char Numb1;
+	char Numb2;
+
+	if (ch1 >= 'A')
+		Numb1 = (toupper(ch1) - '0' - 7) * 16;
+	else
+		Numb1 = (ch1 - '0') * 16;
+
+	if (ch2 >= 'A')
+		Numb2 = (toupper(ch2) - '0' - 7);
+	else
+		Numb2 = (ch2 - '0');
+
+	return (Numb1 + Numb2);
+}
+
+std::string HexString2Bytes(std::string strHex, const int &len /* = 3 */)
+{
+	byte value = 0;
+	std::string strBytes;
+	int nSize = strHex.size();
+	for (int i = 0; i < nSize; i += len)
+	{
+		strBytes.push_back(TwoHexChar2Char(strHex[i], strHex[i + 1]));
+	}
+
+	return strBytes;
+}
+
+std::string Bytes2HexString(const unsigned char *pbuffer, int nLen, char chSpace)
+{
+	std::string hex;
+	char szhex[5] = {0};
+	for (int i = 0; i < nLen; i++)
+	{
+		memset(szhex, 0, 5);
+		_stprintf_s(szhex, "%02X%c", pbuffer[i], chSpace);
+		hex.append(szhex);
+	}
+
+	return hex.substr(0, hex.size() - 1);
+}
+
+std::string Bytes2HexString(const unsigned char *pbuffer, int nLen)
+{
+	std::string hex;
+	char szhex[5] = {0};
+	for (int i = 0; i < nLen; i++)
+	{
+		memset(szhex, 0, 5);
+		_stprintf_s(szhex, "%02X", pbuffer[i]);
+		hex.append(szhex);
+	}
+
+	return hex;
+}
+
+BOOL IsValidString(LPCTSTR lpszString)
+{
+	if (lpszString == NULL)
+		return FALSE;
+
+	do
+	{
+		// ASCII可显示的字符;
+		if (*lpszString < 32 || *lpszString > 126)
+		{
+			return FALSE;
+		}
+	} while (*++lpszString);
+
+	return TRUE;
+}
+
+// 去除空格;
+std::string &trim(std::string &str)
+{
+	int nIndex = 0;
+	while ((nIndex = str.find_first_of(' ')) != std::string::npos)
+		str.erase(nIndex, 1);
+
+	return str;
+}
+
+CSIACP::CSIACP(void) //:m_vtcommand(g_siacp, g_siacp+sizeof(g_siacp)/sizeof(_SIACP_))
+{
+	m_pMainDlg = NULL;
+	m_pSerial = NULL;
+	LoadCommand();
+}
+
+CSIACP::~CSIACP(void)
+{
+	CloseComm();
+}
+
+void CSIACP::SetMainDlg(void* p)
+{
+	m_pMainDlg = p;
+}
+
+void CSIACP::ShowMessage(LPCTSTR lpMsg, LOG_ENUM logtype)
+{
+	if (m_pMainDlg && lpMsg)
+	{
+		//CTCLCopyToolDlg* pMainDlg = (CTCLCopyToolDlg*)m_pMainDlg;
+		//pMainDlg->SetOptionLog(std::string(lpMsg), logtype);
+	}
+}
+
+void CSIACP::LoadCommand()
+{
+	// 解析xml;
+	tinyxml2::XMLDocument doc;
+	if (tinyxml2::XML_SUCCESS != doc.LoadFile(_T("Siacp.xml")))
+	{
+		SaveCommand(g_Siacp, _T("Siacp.xml"));
+
+		g_vtSiacp.clear();
+		for ( int i = 0; i < 42; i++ )
+		{
+			g_vtSiacp.push_back(g_Siacp[i]);
+		}
+		return;
+	}
+
+	std::string ver;
+	std::string desc;
+	g_vtSiacp.clear();
+	tinyxml2::XMLElement *pXmlRoot = NULL;
+	if ((pXmlRoot = doc.RootElement()) != NULL)
+	{
+		if (_tcsicmp(pXmlRoot->Value(), "CommandList") == 0)
+		{
+			// 属性;
+			const tinyxml2::XMLAttribute *pAttr = pXmlRoot->FirstAttribute();
+			while (pAttr)
+			{
+				if (_tcsicmp(pAttr->Name(), "ver") == 0)
+				{
+					ver = pAttr->Value();
+				}
+
+				if (_tcsicmp(pAttr->Name(), "desc") == 0)
+				{
+					desc = pAttr->Value();
+				}
+
+				pAttr = pAttr->Next();
+			}
+
+			// 子项;
+			tinyxml2::XMLElement *pXmlElent = pXmlRoot->FirstChildElement();
+			while (pXmlElent)
+			{
+				_SIACP_ siacp;
+				if (_tcsicmp(pXmlElent->Value(), _T("Item")) == 0)
+				{
+					tinyxml2::XMLElement *pItem = pXmlElent->FirstChildElement();
+					while (pItem)
+					{
+						if (_tcsicmp(pItem->Value(), _T("Name")) == 0)
+						{
+							siacp.name = pItem->GetText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("HeadCode")) == 0)
+						{
+							siacp.head = pItem->GetText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("ucCommand")) == 0)
+						{
+							siacp.code = pItem->GetText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("Mark")) == 0)
+						{
+							siacp.mark = pItem->GetText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("MultiParams")) == 0)
+						{
+							siacp.bMulticode = pItem->BoolText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("ReadWaitTime")) == 0)
+						{
+							siacp.read_wait_time = pItem->IntText();
+						}
+						else if (_tcsicmp(pItem->Value(), _T("CmdWaitTime")) == 0)
+						{
+							siacp.cmd_wait_time = pItem->IntText();
+						}
+
+						pItem = pItem->NextSiblingElement();
+					}
+				}
+				g_vtSiacp.push_back(siacp);
+				pXmlElent = pXmlElent->NextSiblingElement();
+			}
+		}
+	}
+}
+
+void CSIACP::SaveCommand(std::vector<_SIACP_> &vtSiacp, std::string path)
+{
+	tinyxml2::XMLDocument doc;
+	//添加申明可以使用如下两行
+	tinyxml2::XMLDeclaration *declaration = doc.NewDeclaration();
+	doc.InsertFirstChild(declaration);
+
+	tinyxml2::XMLElement *root = doc.NewElement("CommandList");
+	root->SetAttribute("ver", "9.5");
+	root->SetAttribute("desc", "command info");
+	doc.InsertEndChild(root);
+
+	std::vector<_SIACP_>::iterator it = vtSiacp.begin();
+	for (; it != vtSiacp.end(); it++)
+	{
+		tinyxml2::XMLElement *pItem = doc.NewElement("Item");
+
+		tinyxml2::XMLElement *pName = doc.NewElement("Name");
+		pName->SetText(it->name.c_str());
+		pItem->InsertEndChild(pName);
+
+		tinyxml2::XMLElement *pHeadCode = doc.NewElement("HeadCode");
+		pHeadCode->SetText(it->head.c_str());
+		pItem->InsertEndChild(pHeadCode);
+
+		tinyxml2::XMLElement *pucCommand = doc.NewElement("ucCommand");
+		pucCommand->SetText(it->code.c_str());
+		pItem->InsertEndChild(pucCommand);
+
+		tinyxml2::XMLElement *pMark = doc.NewElement("Mark");
+		pMark->SetText(it->mark.c_str());
+		pItem->InsertEndChild(pMark);
+
+		tinyxml2::XMLElement *pMultiParams = doc.NewElement("MultiParams");
+		pMultiParams->SetText(it->bMulticode);
+		pItem->InsertEndChild(pMultiParams);
+
+		tinyxml2::XMLElement *pReadWaitTime = doc.NewElement("ReadWaitTime");
+		pReadWaitTime->SetText(it->read_wait_time);
+		pItem->InsertEndChild(pReadWaitTime);
+
+		tinyxml2::XMLElement* pCmdWaitTime = doc.NewElement("CmdWaitTime");
+		pCmdWaitTime->SetText(it->cmd_wait_time);
+		pItem->InsertEndChild(pCmdWaitTime);
+
+		root->InsertEndChild(pItem);
+	}
+
+	doc.SaveFile(path.c_str());
+}
+
+void CSIACP::SaveCommand(_SIACP_ (&Siacp)[42], std::string path)
+{
+	tinyxml2::XMLDocument doc;
+	//添加申明可以使用如下两行
+	tinyxml2::XMLDeclaration *declaration = doc.NewDeclaration();
+	doc.InsertFirstChild(declaration);
+
+	tinyxml2::XMLElement *root = doc.NewElement("CommandList");
+	root->SetAttribute("ver", "9.5");
+	root->SetAttribute("desc", "command info");
+	doc.InsertEndChild(root);
+
+	for (int i = 0; i < 42; i++)
+	{
+		tinyxml2::XMLElement *pItem = doc.NewElement("Item");
+
+		tinyxml2::XMLElement *pName = doc.NewElement("Name");
+		pName->SetText(Siacp[i].name.c_str());
+		pItem->InsertEndChild(pName);
+
+		tinyxml2::XMLElement *pHeadCode = doc.NewElement("HeadCode");
+		pHeadCode->SetText(Siacp[i].head.c_str());
+		pItem->InsertEndChild(pHeadCode);
+
+		tinyxml2::XMLElement *pucCommand = doc.NewElement("ucCommand");
+		pucCommand->SetText(Siacp[i].code.c_str());
+		pItem->InsertEndChild(pucCommand);
+
+		tinyxml2::XMLElement *pMark = doc.NewElement("Mark");
+		pMark->SetText(Siacp[i].mark.c_str());
+		pItem->InsertEndChild(pMark);
+
+		tinyxml2::XMLElement *pMultiParams = doc.NewElement("MultiParams");
+		pMultiParams->SetText(Siacp[i].bMulticode);
+		pItem->InsertEndChild(pMultiParams);
+
+		tinyxml2::XMLElement *pReadWaitTime = doc.NewElement("ReadWaitTime");
+		pReadWaitTime->SetText(Siacp[i].read_wait_time);
+		pItem->InsertEndChild(pReadWaitTime);
+
+		tinyxml2::XMLElement* pCmdWaitTime = doc.NewElement("CmdWaitTime");
+		pCmdWaitTime->SetText(Siacp[i].cmd_wait_time);
+		pItem->InsertEndChild(pCmdWaitTime);
+
+		root->InsertEndChild(pItem);
+	}
+
+	doc.SaveFile(path.c_str());
+}
+
+BOOL CSIACP::OpenComm(int nSerialPort, DWORD dwBaudrate)
+{
+	if ( nSerialPort <= 0 )
+	{
+		////Global::WriteReportLog(1, "Parameter error", "(lpszCom) Invalid parameter!");
+		return FALSE;
+	}
+
+	// 释放打开的;
+	if (m_pSerial)
+		delete m_pSerial;
+	m_pSerial = NULL;
+
+	m_pSerial = new CSynSerial();
+	if (m_pSerial == NULL)
+	{
+		//Global::WriteReportLog(1, "Memory error", "(m_pSerial) Fail to create object!");
+		return FALSE;
+	}
+
+	m_pSerial->OpenSerialPort(nSerialPort, dwBaudrate, 8, 0, 1, 0, 1000);
+	if (m_pSerial->IsOpen() == FALSE)
+	{
+		delete m_pSerial;
+		m_pSerial = NULL;
+		//Global::WriteReportLog(1, "Serial port error", "Open Serial Port Fail !");
+		return FALSE;
+	}
+
+	// 超时值设置;
+	COMMTIMEOUTS comTimeOut;
+	comTimeOut.ReadIntervalTimeout = 50;
+	comTimeOut.ReadTotalTimeoutConstant = 8000;   // 读取所有字节总超时值;
+	comTimeOut.ReadTotalTimeoutMultiplier = 100;  // 读取1个字节超时值;
+	comTimeOut.WriteTotalTimeoutConstant = 5000;  // 写所有字节的总超时值;
+	comTimeOut.WriteTotalTimeoutMultiplier = 100; // 写1个字节的超时值;
+	m_pSerial->SetTimeouts(comTimeOut);
+
+	return TRUE;
+}
+
+void CSIACP::CloseComm()
+{
+	if (m_pSerial)
+		delete m_pSerial;
+	m_pSerial = NULL;
+}
+
+BOOL CSIACP::ExecSerialCommand(const _SIACP_ &siacp, std::string command)
+{
+	if (m_pSerial == NULL || !m_pSerial->IsOpen())
+	{
+		//Global::WriteReportLog(1, "Serial port error", "Serial Port un't Open!");
+		return FALSE;
+	}
+
+	byte szRecive[BUFFER_LEN] = {0};
+	DWORD dwWritten = m_pSerial->WriteComm((BYTE*)command.c_str(), command.size());
+	//Global::WriteTextLog(_T("COM Write:%s"), Bytes2HexString((const byte *)command.c_str(), command.size(), ' ').c_str());
+	if (dwWritten != command.size())
+	{
+		//Global::WriteTextLog("COM Written Error");
+		//Global::WriteReportLog(1, "Serial Writing Error", "COM Written Error, Buffer=%s", Bytes2HexString((const byte*)command.c_str(), command.size(), ' ').c_str());
+		return FALSE;
+	}
+
+	// 暂停时间;// 等待设备响应完成;
+	Sleep(siacp.read_wait_time);
+	DWORD dwBytesRead = m_pSerial->ReadComm(szRecive, BUFFER_LEN);
+	if (dwBytesRead == 0)
+	{
+		//Global::WriteTextLog("COM Read Error");
+		//Global::WriteReportLog(1, "Serial Reading Error","COM Read Error(No Data Return), Write=%s!", Bytes2HexString((const byte*)command.c_str(), command.size(), ' ').c_str());
+		return FALSE;
+	}
+
+	//Global::WriteTextLog(_T("COM Read:%s\r"), Bytes2HexString(szRecive, dwBytesRead, ' ').c_str());
+
+	// 下一条命令等待时间;
+	Sleep(siacp.cmd_wait_time);
+	// 解析数据;
+	return ParserSerialData(m_vtdata, siacp, szRecive, dwBytesRead);
+}
+
+BOOL CSIACP::ParserSerialData(std::vector<RTN> &vtdata, const _SIACP_ &siacp, byte *pbuffer, DWORD dwlen)
+{
+	vtdata.clear();
+	if (pbuffer == NULL || dwlen < 5)
+	{
+		//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return length less than 5! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+		return FALSE;
+	}
+
+	// 返回码;
+	byte byReturn = TV_Return;
+	byte byHead = (byte)HexString2Bytes(siacp.head)[0];
+	byte byCode = (byte)HexString2Bytes(siacp.code)[0];
+
+	if (byHead == TV_Debug)
+	{
+		byReturn = TV_Return;
+	}
+	else if (byHead == TV_Panel_Debug)
+	{
+		byReturn = TV_Panel_Return;
+	}
+	else if (byHead == TV_Debug_Other)
+	{
+		byReturn = TV_Other_Return;
+	}
+	else
+	{
+		//Global::WriteTextLog("COM Read Parser: Invalid return code!");
+		//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Invalid return code! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+		return FALSE;
+	}
+
+	std::string data;
+	data.append((const char *)pbuffer, dwlen);
+
+	int nABFE = 0;
+	std::string package;
+	int package_len = 0;
+	// 已取出的长度;
+	int nTookenLen = 0;
+	unsigned short usCRCValue;
+	bool mark = true;
+	while (true)
+	{
+begin:
+		if (mark)
+		{
+			while ((byte)data.at(0) != byReturn)
+			{
+				data.erase(0, 1);
+				dwlen--;
+				if (dwlen < 5)
+				{
+					//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return data error! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+					return FALSE;
+				}
+			}
+			while ((byte)data.at(1) != 0x05)
+			{
+				data.erase(0, 2);
+				dwlen = dwlen - 2;
+				if (dwlen < 5)
+				{
+					//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return data error! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+					return FALSE;
+				}
+				goto begin;
+			}
+			while ((byte)data.at(2) != 0x0A)
+			{
+				data.erase(0, 3);
+				dwlen = dwlen - 3;
+				if (dwlen < 5)
+				{
+					//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return data error! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+					return FALSE;
+				}
+				goto begin;
+			}
+			while ((byte)data.at(3) != 0xDF)
+			{
+				data.erase(0, 4);
+				dwlen = dwlen - 4;
+				if (dwlen < 5)
+				{
+					//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return data error! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+					return FALSE;
+				}
+				goto begin;
+			}
+			while ((byte)data.at(4) != 0x4E)
+			{
+				data.erase(0, 5);
+				dwlen = dwlen - 5;
+				if (dwlen < 5)
+				{
+					//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Return data error! Buffer = %s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+					return FALSE;
+				}
+				goto begin;
+			}
+		}
+		mark = false;
+		// 是否已取完所有数据;
+		if (nTookenLen >= dwlen)
+			break;
+
+		// 取出长度;
+		if ((byte)data.at(nTookenLen + 1) == 0xFE)
+		{
+			nABFE = 2; //FE占1字节,长度多占1字节;共多占2字节;
+			package_len = ((unsigned int)data[nTookenLen + 2] << 8) | (byte)data[nTookenLen + 3];
+		}
+		else
+		{
+			nABFE = 0;
+			package_len = data[nTookenLen + 1];
+		}
+
+		// 包长度是否在有效长度内;
+		if (package_len > (dwlen - nTookenLen))
+		{
+			//Global::WriteTextLog("COM Read Parser: Incorrect length of returned data!");
+			//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Incorrect length of returned data! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+			return FALSE;
+		}
+
+		// 取出数据包;
+		package = data.substr(nTookenLen, nTookenLen + package_len);
+
+		// 校验包头是否正确;
+		if (byte(package[0]) != byReturn)
+		{
+			//Global::WriteTextLog("COM Read Parser: Data Header Check Error!");
+			//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Data Header Check Error! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+			return FALSE;
+		}
+
+		// 校验crc;
+		usCRCValue = CRC16Calculate((unsigned char *)package.data(), package_len - 2);
+		if (((usCRCValue >> 8) & 0xFF) != (unsigned char)package[package_len - 2] || (usCRCValue & 0xFF) != (unsigned char)package[package_len - 1])
+		{
+			//Global::WriteTextLog("COM Read Parser: Data CRC validation failure!");
+			//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,Data CRC validation failure! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+			return FALSE;
+		}
+
+		// 第一组返回,检测结果码
+		if (nTookenLen == 0)
+		{
+			if (package[2] != RC_OK)
+			{
+				//Global::WriteTextLog("COM Read Parser: The return code of the response data is invalid!");
+				//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,The return code of the response data is invalid! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+				return FALSE;
+			}
+		}
+		else
+		{
+			// 响应数据的命令码 = 发送数据的命令码 - 1;
+			if (byte(package[2 + nABFE] - 1) != byCode)
+			{
+				//Global::WriteTextLog("COM Read Parser: The command code of response data does not match the command code of sending data!");
+				//Global::WriteReportLog(1, "Serial Parsing error", "cmd=%s,The command code of response data does not match the command code of sending data! Buffer=%s", siacp.name.c_str(), Bytes2HexString(pbuffer, dwlen, ' ').c_str());
+				return FALSE;
+			}
+
+			RTN rtn;
+			// 获取结果值;
+			if (siacp.bMulticode)
+			{
+				rtn.len = package_len - 6 - nABFE;
+				rtn.data = package.substr(4 + nABFE, package_len - 6 - nABFE);
+			}
+			else
+			{
+				rtn.len = package_len - 5 - nABFE;
+				rtn.data = package.substr(3 + nABFE, package_len - 5 - nABFE);
+			}
+			vtdata.push_back(rtn);
+			break;//最多解析2段
+		}
+
+		nTookenLen += package_len;
+	}
+
+	return TRUE;
+}
+
+const _SIACP_ *CSIACP::GetSiacp(std::string name)
+{
+	std::vector<_SIACP_>::iterator it = g_vtSiacp.begin();
+	for (; it != g_vtSiacp.end(); it++)
+	{
+		if (_tcsicmp(it->name.c_str(), name.c_str()) == 0)
+		{
+			return &*it;
+		}
+	}
+
+	return NULL;
+}
+
+std::string CSIACP::GetSiacpCommand(const _SIACP_ &siacp, std::string others, int othersLen)
+{
+	std::string command;
+	command.append(HexString2Bytes(siacp.head).c_str(), (siacp.head.size() + 1) / 3);
+	command.append(HexString2Bytes(siacp.code).c_str(), (siacp.code.size() + 1) / 3);
+	command.append(HexString2Bytes(siacp.mark).c_str(), (siacp.mark.size() + 1) / 3);
+	command.append(others.c_str(), othersLen);
+
+	// 长度;
+	byte szlen[2] = {0};
+	int len = command.size() + 3; // 3=长度本身占1位+2位crc;
+	if ((byte)command[1] == 0xFE)
+	{
+		szlen[0] = ((len + 1) >> 8) & 0xFF;
+		szlen[1] = (len + 1) & 0xFF;
+		command.insert(2, (char *)szlen, 2);
+	}
+	else
+	{
+		szlen[0] = len & 0xFF;
+		command.insert(1, (char *)szlen, 1);
+	}
+
+	// crc校验;
+	byte szcrc[2] = {0};
+	WORD usCRCValue = CRC16Calculate((byte *)command.c_str(), command.size());
+	szcrc[0] = (usCRCValue >> 8) & 0xFF;
+	szcrc[1] = usCRCValue & 0xFF;
+	command.append((char *)szcrc, 2);
+
+	return command;
+}
+
+std::string CSIACP::GetSiacpCommand(std::string name, std::string others, int othersLen)
+{
+	std::string command;
+	std::vector<_SIACP_>::iterator it = g_vtSiacp.begin();
+	for (; it != g_vtSiacp.end(); it++)
+	{
+		if (_tcsicmp(name.c_str(), it->name.c_str()) == 0)
+		{
+			command.append(HexString2Bytes(it->head).c_str(), (it->head.size() + 1) / 3);
+			command.append(HexString2Bytes(it->code).c_str(), (it->code.size() + 1) / 3);
+			command.append(HexString2Bytes(it->mark).c_str(), (it->mark.size() + 1) / 3);
+			command.append(others.c_str(), othersLen);
+
+			// 长度;
+			byte szlen[2] = {0};
+			int len = command.size() + 3; // 长度本身占1位;
+			if ((byte)command[1] == 0xFE)
+			{
+				szlen[0] = ((len + 1) >> 8) & 0xFF;
+				szlen[1] = (len + 1) & 0xFF;
+				command.insert(2, (char *)szlen, 2);
+			}
+			else
+			{
+				szlen[0] = len & 0xFF;
+				command.insert(1, (char *)szlen, 1);
+			}
+
+			// crc校验;
+			byte szcrc[2] = {0};
+			int usCRCValue = CRC16Calculate((byte *)command.c_str(), command.size());
+			szcrc[0] = (usCRCValue >> 8) & 0xFF;
+			szcrc[1] = usCRCValue & 0xFF;
+			command.append((char *)szcrc, 2);
+
+			break;
+		}
+	}
+
+	return command;
+}
+
+BOOL CSIACP::SendCommand(std::string cmd_name, const byte *pszOthers, DWORD dwOthersLen)
+{
+	std::string others;
+	if (pszOthers && dwOthersLen > 0)
+	{
+		others.append((char *)pszOthers, dwOthersLen);
+	}
+
+	// 获取命令协议;
+	const _SIACP_ *pSiacp = GetSiacp(cmd_name);
+	if (pSiacp == NULL)
+	{
+		//Global::WriteTextLog("This command is not supported!");
+		//Global::WriteReportLog(1, "Invalid command", "This command(%s) is not supported!", cmd_name.c_str());
+		return FALSE;
+	}
+
+	std::string command = GetSiacpCommand(*pSiacp, others, dwOthersLen);
+	if (command.size() == 0)
+	{
+		//Global::WriteTextLog("Generation command failed!");
+		//Global::WriteReportLog(1, "Generation command failed", "command=%s", cmd_name.c_str());
+		return FALSE;
+	}
+
+	return ExecSerialCommand(*pSiacp, command);
+}
+
+BOOL CSIACP::SendCommand2(std::string cmd_name, std::string data /* = "" */, size_t nlen /* = 0 */)
+{
+	// 获取命令协议;
+	const _SIACP_ *pSiacp = GetSiacp(cmd_name);
+	if (pSiacp == NULL)
+	{
+		//Global::WriteTextLog("This command is not supported!");
+		//Global::WriteReportLog(1, "Invalid command", "This command(%s) is not supported!", cmd_name.c_str());
+		return FALSE;
+	}
+
+	std::string command = GetSiacpCommand(*pSiacp, data, nlen);
+	if (command.size() == 0)
+	{
+		//Global::WriteTextLog("Generation command failed!");
+		//Global::WriteReportLog(1, "Generation command failed", "command=%s", cmd_name.c_str());
+		return FALSE;
+	}
+
+	return ExecSerialCommand(*pSiacp, command);
+}
+
+BOOL CSIACP::SCBC_WaitTVBoot()
+{
+	byte szRecive[BUFFER_LEN] = { 0 };
+	DWORD dwBytesRead = 0;
+	// 根据配置文件,是否需要读等待;
+	if ( true )
+		dwBytesRead = m_pSerial->ReadComm(szRecive, BUFFER_LEN);
+	if (dwBytesRead == 0)
+	{
+		if ( SCBC_EnterFactory() )
+		{
+			SCBC_LeaveFactory();
+			return TRUE;
+		}		
+	}
+
+	return FALSE;
+}
+
+BOOL CSIACP::SCBC_MTKInit()
+{
+	if (m_pSerial == NULL)
+	{
+		//Global::WriteTextLog("串口对象未初始化");
+		//Global::WriteReportLog(1, "MTKInit Error", "Serial port object uninitialized!");
+		return FALSE;
+	}
+
+	if (!m_pSerial->IsOpen())
+	{
+		//Global::WriteTextLog("未打开串口");
+		//Global::WriteReportLog(1, "MTKInit Error", "Unopened Serial Port!");
+		return FALSE;
+	}
+
+	unsigned char ucBufInitMTK8223[16] = {0x54, 0x8A, 0x51, 0x37, 0x56, 0x92, 0x1C, 0xB2, 0x50, 0x80, 0x50, 0x02, 0x50, 0x01, 0x50, 0xE2};
+	m_pSerial->WriteComm(ucBufInitMTK8223, 16);
+	Sleep(50);
+
+	return TRUE;
+}
+
+// 对外接口;
+BOOL CSIACP::SCBC_EnterFactory()
+{
+	return SendCommand("EnterFactory");
+}
+
+BOOL CSIACP::SCBC_LeaveFactory()
+{
+	return SendCommand("LeaveFactory");
+}
+
+BOOL CSIACP::SCBC_WBInit()
+{
+	return SendCommand("WBInit");
+}
+
+BOOL CSIACP::SCBC_GetProjectId(int &pid)
+{
+	BOOL bRet = SendCommand("GetProjectId");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.len == 2)
+		{
+			pid = (byte)rtn.data[0] << 8;
+			pid += (byte)rtn.data[1];
+		}
+		else
+		{
+			pid = (byte)rtn.data[0];
+		}
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetSoftVersion(std::string &strVer)
+{
+	BOOL bRet = SendCommand("GetSoftVersion");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (!IsValidString(rtn.data.c_str()))
+		{
+			//Global::WriteTextLog("%s:%s%s!", __FUNCTION__, "tv version string is invalid=", rtn.data.c_str());
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s%s!", __FUNCTION__, "tv version string is invalid=", rtn.data.c_str());
+
+			return FALSE;
+		}
+
+		strVer.append(rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetDeviceId(std::string &strDid)
+{
+	BOOL bRet = SendCommand("GetDeviceId");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		strDid.append(rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetClientType(std::string &strClt)
+{
+	BOOL bRet = SendCommand("GetClientType");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (!IsValidString(rtn.data.c_str()))
+		{
+			//Global::WriteTextLog("%s:%s%s!", __FUNCTION__, "tv client string is invalid=", rtn.data.c_str());
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s%s!", __FUNCTION__, "tv client string is invalid=", rtn.data.c_str());
+
+			return FALSE;
+		}
+		strClt.append(rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetMAC(std::string &strMac)
+{
+	BOOL bRet = SendCommand("GetMAC");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		strMac = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len, '-');
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetHDCPKey(std::string &strHDCP)
+{
+	BOOL bRet = SendCommand("GetHDCPKey");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		//strHDCP.append(rtn.data.c_str(), rtn.len);
+		strHDCP = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetHDCPKey22(std::string &strHDCP22)
+{
+	BOOL bRet = SendCommand("GetHDCPKey22");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		//strHDCP22.append(rtn.data.c_str(), rtn.len);
+		strHDCP22 = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetWidi(std::string &strWidi)
+{
+	BOOL bRet = SendCommand("GetWidi");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		//strWidi.append(rtn.data.c_str(), rtn.len);
+		strWidi = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetNetflixESN(std::string &strESN)
+{
+	BOOL bRet = SendCommand("GetNetflixESN");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		strESN.append(rtn.data.c_str(), rtn.len);
+		//strESN = Bytes2HexString((const byte*)rtn.data.c_str(), rtn.len, ' ');
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetWidevine(std::string &strWidevine)
+{
+	BOOL bRet = SendCommand("GetWidevine");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		//strWidevine.append(rtn.data.c_str(), rtn.len);
+		strWidevine = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetCiKey(std::string &strCikey)
+{
+	BOOL bRet = SendCommand("GetCiKey");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		//strCikey.append(rtn.data.c_str(), rtn.len);
+		strCikey = Bytes2HexString((const byte *)rtn.data.c_str(), rtn.len);
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetOSDLanguage(std::string &strOSDLan)
+{
+	BOOL bRet = SendCommand("GetOSDLanguage");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		strOSDLan.append(rtn.data.c_str(), rtn.len);
+		strOSDLan = Bytes2HexString((const byte*)rtn.data.c_str(), rtn.len, ' ');
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetShopLanguage(std::string &strShopLan)
+{
+	BOOL bRet = SendCommand("GetShopLanguage");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		strShopLan.append(rtn.data.c_str(), rtn.len);
+		strShopLan = Bytes2HexString((const byte*)rtn.data.c_str(), rtn.len, ' ');
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_GetChannel(std::string& channel)
+{
+	BOOL bRet = SendCommand("GetChannel");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		channel.append(rtn.data.c_str(), rtn.len);
+		channel = Bytes2HexString((const byte*)rtn.data.c_str(), rtn.len, ' ');
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_SetProjectId(const int &pid)
+{
+	std::string data;
+	if (pid > 0xFF)
+	{
+		data.append(1, (pid >> 8) & 0xFF);
+		data.append(1, pid & 0xFF);
+	}
+	else
+	{
+		data.append(1, pid & 0xFF);
+	}
+
+	return SendCommand2("SetProjectId", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetProjectId(std::string pid)
+{
+	return SCBC_SetProjectId(atoi(pid.c_str()));
+}
+
+BOOL CSIACP::SCBC_SetProjectId(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetProjectId", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetDeviceId(std::string strDeviceId)
+{
+	if (strDeviceId.size() < 40)
+	{
+		return FALSE;
+	}
+
+	return SendCommand2("SetDeviceId", strDeviceId, strDeviceId.size());
+}
+
+BOOL CSIACP::SCBC_SetDeviceId(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetDeviceId", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetMAC(std::string strMac)
+{
+	if (strMac.size() < 16)
+		return FALSE;
+
+	std::string data = HexString2Bytes(strMac);
+	return SendCommand2("SetMAC", data, (strMac.size() + 1) / 3);
+}
+
+BOOL CSIACP::SCBC_SetMAC(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetMAC", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetHDCPKey(std::string strHDCP, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strHDCP, bHasSpace ? 3 : 2);
+	return SendCommand2("SetHDCPKey", data, (strHDCP.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetHDCPKey(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetHDCPKey", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetHDCPKey22(std::string strHDCP22, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strHDCP22, bHasSpace ? 3 : 2);
+	return SendCommand2("SetHDCPKey22", data, (strHDCP22.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetHDCPKey22(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetHDCPKey22", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetNetflixESN(std::string strESN, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strESN, bHasSpace ? 3 : 2);
+	return SendCommand2("SetNetflixESN", data, (strESN.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetNetflixESN(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetNetflixESN", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetWidi(std::string strWidi, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strWidi, bHasSpace ? 3 : 2);
+	return SendCommand2("SetWidi", data, (strWidi.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetWidi(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetWidi", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetWidevine(std::string strWidevine, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strWidevine, bHasSpace ? 3 : 2);
+	return SendCommand2("SetWidevine", data, (strWidevine.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetWidevine(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetWidevine", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetCiKey(std::string strCiKey, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(strCiKey, bHasSpace ? 3 : 2);
+	return SendCommand2("SetCiKey", data, (strCiKey.size() + 1) / (bHasSpace ? 3 : 2));
+}
+
+BOOL CSIACP::SCBC_SetCiKey(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetCiKey", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetOSDLanguage(std::string lan, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(lan, bHasSpace ? 3 : 2);
+	return SendCommand2("SetOSDLanguage", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetOSDLanguage(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetOSDLanguage", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetShopLanguage(std::string lan, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(lan, bHasSpace ? 3 : 2);
+	return SendCommand2("SetShopLanguage", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetShopLanguage(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetShopLanguage", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetChannel(std::string channel, BOOL bHasSpace /* = TRUE */)
+{
+	std::string data = HexString2Bytes(channel, bHasSpace ? 3 : 2);
+	return SendCommand2("SetChannel", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetChannel(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetChannel", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetWBNormal(std::string data)
+{
+	return SendCommand2("SetWBNormal", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetWBNormal(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetWBNormal", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetWBCool(std::string data)
+{
+	return SendCommand2("SetWBCool", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetWBCool(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetWBCool", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_SetWBWarm(std::string data)
+{
+	return SendCommand2("SetWBWarm", data, data.size());
+}
+
+BOOL CSIACP::SCBC_SetWBWarm(const byte *pBuffer, const int &nLen)
+{
+	return SendCommand("SetWBWarm", pBuffer, nLen);
+}
+
+BOOL CSIACP::SCBC_CheckDeviceId()
+{
+	BOOL bRet = SendCommand("CheckDeviceId");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+#if TEMP_5659
+		if (bydata == 0x03 || bydata == 0x02 || bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+#else
+		if (bydata == 0x02 || bydata == 0x03 || bydata == 0x07)
+			return TRUE;
+		else
+			return FALSE;
+#endif
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckMAC()
+{
+	BOOL bRet = SendCommand("CheckMAC");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01 || bydata == 0x03 || bydata == 0x07)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckHDCP()
+{
+	BOOL bRet = SendCommand("CheckHDCP");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckHDCP22()
+{
+	BOOL bRet = SendCommand("CheckHDCP22");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckNetflixESN()
+{
+	BOOL bRet = SendCommand("CheckNetflixESN");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckWidi()
+{
+	BOOL bRet = SendCommand("CheckWidi");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckWidevine()
+{
+	BOOL bRet = SendCommand("CheckWidevine");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_CheckCikey()
+{
+	BOOL bRet = SendCommand("CheckCikey");
+	if (bRet)
+	{
+		if (m_vtdata.size() == 0)
+		{
+			//Global::WriteTextLog("%s:%s", __FUNCTION__, "No data return!");
+			//Global::WriteReportLog(1, "Copy Key", "%s:%s", __FUNCTION__, "No data return!");
+
+			return FALSE;
+		}
+
+		RTN rtn = m_vtdata.at(0);
+		if (rtn.data.size() == 0)
+		{
+			return FALSE;
+		}
+
+		byte bydata = (byte)rtn.data.at(0);
+		if (bydata == 0x01)
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	return bRet;
+}
+
+BOOL CSIACP::SCBC_StarWarmUpMode()
+{
+	return SendCommand("StarWarmUpMode");
+}
+
+BOOL CSIACP::SCBC_StopWarmUpMode()
+{
+	return SendCommand("StopWarmUpMode");
+}

+ 178 - 0
ScbcCopyKey/ScbcCopyKey/OTA.h

@@ -0,0 +1,178 @@
+#ifndef __OTA_20190702__
+#define __OTA_20190702__
+
+#include "SynSerial.h"
+#include <list>
+
+#define BUFFER_LEN 10240
+#pragma once
+
+typedef struct __SIACP__
+{
+	std::string name; // 命令描述;
+	std::string head; // 命令头;
+	std::string code; // 命令码;
+	std::string mark; // 特殊标记;
+	bool bMulticode ;	// 命令码是否多参数;
+	int cmd_wait_time ;	// 两条串口指令间隔时间;
+	int read_wait_time ;	// 写完串口后,等待多久读;
+} _SIACP_, *_pSIACP_;
+
+
+// 包头码(包引导码)
+enum PHeader {
+	TV_Debug = 0xAA,
+	TV_Return = 0xAB,
+	TV_Panel_Debug = 0xAC,
+	TV_Panel_Return = 0xAD,
+	TV_Debug_Other = 0xAE,
+	TV_Other_Return = 0xAF,
+};
+
+// 命令结果码;
+enum RCode {
+	RC_OK = 0x0A,      // 命令执行通过;
+	RC_ERR = 0x0E,     // 命令错误或无法执行;
+	RC_LOSE = 0x0F,    // 命令丢失(链路层出错);
+};
+
+//////////////////////////////////////////////////////////////////////////
+enum LOG_ENUM
+{
+	NORMAL_LOG = 0,
+	ERROR_LOG,
+	OK_LOG,
+	INFO_LOG
+};
+
+extern std::string HexString2Bytes(std::string strHex, const int& len /* = 3 */);
+extern std::string Bytes2HexString(const unsigned char* pbuffer, int nLen);
+extern std::string Bytes2HexString(const unsigned char* pbuffer, int nLen, char chSpace);
+
+class CSIACP
+{
+public:
+	CSIACP(void);
+	~CSIACP(void);
+
+	void SetMainDlg(void* p);
+	void ShowMessage(LPCTSTR lpMsg, LOG_ENUM logtype = NORMAL_LOG);
+protected:
+	CSynSerial* m_pSerial;
+	void* m_pMainDlg;
+	typedef struct __RTN__
+	{
+		int len;			// 返回的数据长度;
+		std::string data;	// 返回的数据内容;	
+
+		__RTN__()
+		{
+			len = 0;	
+		}
+	}RTN, * pRTN;
+
+	std::vector<RTN> m_vtdata;
+	std::vector<_SIACP_> m_vtcommand;
+	BOOL ExecSerialCommand(const _SIACP_& siacp, std::string command);
+	BOOL ParserSerialData(std::vector<RTN>& vtdata, const _SIACP_& siacp, byte* pbuffer, DWORD dwlen);
+
+	const _SIACP_* GetSiacp(std::string name);
+	std::string GetSiacpCommand(const _SIACP_& siacp, std::string others, int othersLen);
+	std::string GetSiacpCommand(std::string name, std::string others, int othersLen);
+
+public:
+	void LoadCommand();
+	void SaveCommand(std::vector<_SIACP_> &vtSiacp, std::string path);
+	void SaveCommand(_SIACP_ (&Siacp)[42], std::string path);
+	BOOL OpenComm(int nSerialPort, DWORD dwBaudrate);
+	void CloseComm();
+	BOOL IsOpen() {
+		return m_pSerial && m_pSerial->IsOpen();
+	}
+	BOOL SendCommand(std::string cmd_name, const byte* pszOthers = NULL, DWORD dwOthersLen = 0);
+	BOOL SendCommand2(std::string cmd_name, std::string data = "", size_t nlen = 0);
+public:
+	// 等待tv启动成功;
+	BOOL SCBC_WaitTVBoot();
+	BOOL SCBC_MTKInit();
+	// 进入工厂模式;
+	BOOL SCBC_EnterFactory();
+	// 离开工厂模式;
+	BOOL SCBC_LeaveFactory();
+	// 初始化Wb;
+	BOOL SCBC_WBInit();
+	// 获取pid;
+	BOOL SCBC_GetProjectId(int& pid);
+	// 软件版本号;
+	BOOL SCBC_GetSoftVersion(std::string& strVer);
+	// 设备ID
+	BOOL SCBC_GetDeviceId(std::string& strDid);
+	// ClientType;
+	BOOL SCBC_GetClientType(std::string& strClt);
+	// MAC地址;
+	BOOL SCBC_GetMAC(std::string& strMac);
+	// HDCP Key;
+	BOOL SCBC_GetHDCPKey(std::string& strHDCP);
+	// HDCP Key2.2;
+	BOOL SCBC_GetHDCPKey22(std::string& strHDCP22);
+	// Widi;
+	BOOL SCBC_GetWidi(std::string& strWidi);
+	// Netflix ESN;
+	BOOL SCBC_GetNetflixESN(std::string& strESN);
+	// Widevine;
+	BOOL SCBC_GetWidevine(std::string& strWidevine);
+	// ci plus key;
+	BOOL SCBC_GetCiKey(std::string& strCikey);
+	// OSD Language;
+	BOOL SCBC_GetOSDLanguage(std::string& strOSDLan);
+	// Shop Language;
+	BOOL SCBC_GetShopLanguage(std::string& strShopLan);
+	BOOL SCBC_GetChannel(std::string& channel);
+
+	BOOL SCBC_SetProjectId(const int& pid);
+	BOOL SCBC_SetProjectId(std::string pid);
+	BOOL SCBC_SetProjectId(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetDeviceId(std::string strDeviceId);
+	BOOL SCBC_SetDeviceId(const byte* pBuffer, const int &nLen);
+	BOOL SCBC_SetMAC(std::string strMac);
+	BOOL SCBC_SetMAC(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetHDCPKey(std::string strHDCP, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetHDCPKey(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetHDCPKey22(std::string strHDCP22, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetHDCPKey22(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetNetflixESN(std::string strESN, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetNetflixESN(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetWidi(std::string strWidi, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetWidi(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetWidevine(std::string strWidevine, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetWidevine(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetCiKey(std::string strCiKey, BOOL bHasSpace = FALSE);
+	BOOL SCBC_SetCiKey(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetOSDLanguage(std::string lan, BOOL bHasSpace = TRUE);
+	BOOL SCBC_SetOSDLanguage(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetShopLanguage(std::string lan, BOOL bHasSpace = TRUE);
+	BOOL SCBC_SetShopLanguage(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetChannel(std::string channel, BOOL bHasSpace = TRUE);
+	BOOL SCBC_SetChannel(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetWBNormal(std::string data);
+	BOOL SCBC_SetWBNormal(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetWBCool(std::string data);
+	BOOL SCBC_SetWBCool(const byte* pBuffer, const int& nLen);
+	BOOL SCBC_SetWBWarm(std::string data);
+	BOOL SCBC_SetWBWarm(const byte* pBuffer, const int& nLen);
+
+	BOOL SCBC_CheckDeviceId();
+	BOOL SCBC_CheckMAC();
+	BOOL SCBC_CheckHDCP();
+	BOOL SCBC_CheckHDCP22();
+	BOOL SCBC_CheckNetflixESN();
+	BOOL SCBC_CheckWidi();
+	BOOL SCBC_CheckWidevine();
+	BOOL SCBC_CheckCikey();
+
+	BOOL SCBC_StarWarmUpMode();
+	BOOL SCBC_StopWarmUpMode();
+};
+
+extern std::string HexString2Bytes(std::string strHex, const int& len = 3);
+#endif

+ 48 - 0
ScbcCopyKey/ScbcCopyKey/ReadMe.txt

@@ -0,0 +1,48 @@
+========================================================================
+    动态链接库:ScbcCopyKey 项目概述
+========================================================================
+
+应用程序向导已为您创建了此 ScbcCopyKey DLL。
+
+本文件概要介绍组成 ScbcCopyKey 应用程序的
+的每个文件的内容。
+
+
+ScbcCopyKey.vcproj
+    这是使用应用程序向导生成的 VC++ 项目的主项目文件,
+    其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
+
+ScbcCopyKey.cpp
+    这是主 DLL 源文件。
+
+	此 DLL 在创建时不导出任何符号。因此,在生成此 DLL 时
+	将不会产生 .lib 文件。如果希望此项目
+	成为其他某个项目的项目依赖项,则需要
+	添加代码以从 DLL 导出某些符号,
+	以便产生一个导出库,或者,也可以在项目“属性页”对话框中的
+	“链接器”文件夹中,将“常规”属性页上的
+	“忽略输入库”属性设置为“是”。
+
+/////////////////////////////////////////////////////////////////////////////
+应用程序向导创建了下列资源:
+
+ScbcCopyKey.rc
+这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。
+此文件可以直接在 Microsoft Visual C++ 中进行编辑。
+
+Resource.h
+    这是标准头文件,可用于定义新的资源 ID。
+    Microsoft Visual C++ 将读取并更新此文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他标准文件:
+
+StdAfx.h, StdAfx.cpp
+    这些文件用于生成名为 ScbcCopyKey.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
+
+/////////////////////////////////////////////////////////////////////////////
+其他注释:
+
+应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
+
+/////////////////////////////////////////////////////////////////////////////

+ 17 - 0
ScbcCopyKey/ScbcCopyKey/Resource.h

@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ScbcCopyKey.rc
+//
+
+#define IDS_APP_TITLE			103
+
+// жÔÏóµÄÏÂÒ»×éĬÈÏÖµ
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE	101
+#define _APS_NEXT_COMMAND_VALUE		40001
+#define _APS_NEXT_CONTROL_VALUE		1000
+#define _APS_NEXT_SYMED_VALUE		101
+#endif
+#endif

+ 1042 - 0
ScbcCopyKey/ScbcCopyKey/ScbcCopyKey.cpp

@@ -0,0 +1,1042 @@
+// ScbcCopyKey.cpp : 定义 DLL 应用程序的导出函数。
+//
+
+#include "stdafx.h"
+#include "ScbcCopyKey.h"
+#include "OTA.h"
+#ifdef __MAKE_PYD__
+#include "Python.h"
+#endif
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+CSIACP g_objSiacp;
+std::string g_strData;
+
+bool ReadKeyFile(const char *lpfile, std::string& data)
+{
+	if (!lpfile || _taccess(lpfile, 0) == -1)
+		return false;
+
+	FILE* pf = NULL;
+	if (_tfopen_s(&pf, lpfile, _T("rb")) == 0)
+	{
+		if (pf)
+		{
+			fseek(pf, 0, SEEK_END);
+			size_t fs = ftell(pf);
+			fseek(pf, 0, SEEK_SET);
+
+			byte* pdata = (byte*)malloc(fs);
+			if (pdata)
+			{
+				fread(pdata, fs, 1, pf);
+				fclose(pf);
+
+				data.append((char*)pdata, fs);
+				free(pdata);
+
+				return true;
+			}
+
+			fclose(pf);
+		}
+	}
+
+	return false;
+}
+
+// 唯一的应用程序对象
+#ifdef __CONSOLE__
+CWinApp theApp;
+
+using namespace std;
+
+int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
+{
+	int nRetCode = 0;
+
+	// 初始化 MFC 并在失败时显示错误
+	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
+	{
+		// TODO: 更改错误代码以符合您的需要
+		_tprintf(_T("错误: MFC 初始化失败\n"));
+		nRetCode = 1;
+	}
+	else
+	{
+		// TODO: 在此处为应用程序的行为编写代码。
+		if ( Open(5, 115200, 8, 0, 1) )
+		{
+			if ( EnterFactory() )
+			{
+				printf("%d\n",GetProjectId());
+				printf("%s\n",GetSoftVersion());
+				printf("%s\n",GetDeviceId());
+				printf("%s\n",GetClientType());
+				printf("%s\n",GetMAC());
+				printf("%s\n",GetHDCPKey());
+				printf("%s\n",GetHDCPKey22());
+				printf("%s\n",GetWidi());
+				printf("%s\n",GetNetflixESN());
+				printf("%s\n",GetWidevine());
+				printf("%s\n",GetCiKey());
+				printf("%s\n",GetOSDLanguage());
+				printf("%s\n",GetShopLanguage());
+				printf("%s\n",GetChannel());
+				LeaveFactory();
+			}
+			
+			Close();
+		}
+	}
+
+	system("pause");
+
+	return nRetCode;
+}
+#endif
+
+#ifndef __MAKE_PYD__
+
+SCBCCOPYKEY_API BOOL Open(int nPort, DWORD dwBaudrate, BYTE ByteSize, BYTE Parity, BYTE StopBits)
+{
+	return g_objSiacp.OpenComm(nPort, dwBaudrate);
+}
+
+SCBCCOPYKEY_API void Close()
+{
+	g_objSiacp.CloseComm();
+}
+
+SCBCCOPYKEY_API BOOL IsOpen()
+{
+	return g_objSiacp.IsOpen();
+}
+
+SCBCCOPYKEY_API BOOL EnterFactory()
+{
+	return g_objSiacp.SCBC_EnterFactory();
+}
+
+SCBCCOPYKEY_API BOOL LeaveFactory()
+{
+	return g_objSiacp.SCBC_LeaveFactory();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 读取;
+SCBCCOPYKEY_API INT GetProjectId()
+{
+	int pid = 0;
+	if ( g_objSiacp.SCBC_GetProjectId(pid) )
+		return pid;
+
+	// 获取失败返回-1;
+	return -1;
+}
+
+SCBCCOPYKEY_API LPCTSTR GetSoftVersion()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetSoftVersion(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetDeviceId()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetDeviceId(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetClientType()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetClientType(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetMAC()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetMAC(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetHDCPKey()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetHDCPKey(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetHDCPKey22()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetHDCPKey22(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetWidi()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetWidi(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetNetflixESN()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetNetflixESN(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetWidevine()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetWidevine(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetCiKey()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetCiKey(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetOSDLanguage()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetOSDLanguage(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetShopLanguage()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetShopLanguage(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+SCBCCOPYKEY_API LPCTSTR GetChannel()
+{
+	g_strData.clear();
+	if ( g_objSiacp.SCBC_GetChannel(g_strData) )
+		return g_strData.c_str();
+
+	return _T("");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 设置;
+SCBCCOPYKEY_API BOOL SetProjectId(int pid)
+{
+	return g_objSiacp.SCBC_SetProjectId(pid);
+}
+
+SCBCCOPYKEY_API BOOL SetProjectId(LPCTSTR lpPid)
+{
+	if ( lpPid == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetProjectId(lpPid);
+}
+
+SCBCCOPYKEY_API BOOL SetProjectId(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetProjectId(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetDeviceId(LPCTSTR lpDeviceId)
+{
+	if ( lpDeviceId == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetDeviceId(lpDeviceId);
+}
+
+SCBCCOPYKEY_API BOOL SetDeviceId(const byte* pBuffer, const int &nLen)
+{
+	return g_objSiacp.SCBC_SetDeviceId(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetMAC(LPCTSTR lpMac)
+{
+	if ( lpMac == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetMAC(lpMac);
+}
+
+SCBCCOPYKEY_API BOOL SetMAC(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetMAC(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetHDCPKey(LPCTSTR lpHDCP, BOOL bHasSpace)
+{
+	if ( lpHDCP == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetHDCPKey(lpHDCP, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetHDCPKey(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetHDCPKey(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetHDCPKey22(LPCTSTR lpHDCP22, BOOL bHasSpace)
+{
+	if ( lpHDCP22 == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetHDCPKey22(lpHDCP22, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetHDCPKey22(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetHDCPKey22(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetNetflixESN(LPCTSTR lpESN, BOOL bHasSpace)
+{
+	if ( lpESN == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetNetflixESN(lpESN, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetNetflixESN(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetNetflixESN(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetWidi(LPCTSTR lpWidi, BOOL bHasSpace)
+{
+	if ( lpWidi == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetWidi(lpWidi, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetWidi(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetWidi(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetWidevine(LPCTSTR lpWidevine, BOOL bHasSpace)
+{
+	if ( lpWidevine == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetWidevine(lpWidevine, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetWidevine(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetWidevine(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetCiKey(LPCTSTR lpCiKey, BOOL bHasSpace)
+{
+	if ( lpCiKey == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetCiKey(lpCiKey, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetCiKey(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetCiKey(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetOSDLanguage(LPCTSTR lan, BOOL bHasSpace)
+{
+	if ( lan == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetOSDLanguage(lan, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetOSDLanguage(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetOSDLanguage(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetShopLanguage(LPCTSTR lan, BOOL bHasSpace)
+{
+	if ( lan == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetShopLanguage(lan, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetShopLanguage(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetShopLanguage(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetChannel(LPCTSTR channel, BOOL bHasSpace)
+{
+	if ( channel == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetChannel(channel, bHasSpace);
+}
+
+SCBCCOPYKEY_API BOOL SetChannel(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetChannel(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetWBNormal(LPCTSTR data)
+{
+	if ( data == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetWBNormal(data);
+}
+
+SCBCCOPYKEY_API BOOL SetWBNormal(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetWBNormal(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetWBCool(LPCTSTR data)
+{
+	if ( data == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetWBCool(data);
+}
+
+SCBCCOPYKEY_API BOOL SetWBCool(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetWBCool(pBuffer, nLen);
+}
+
+SCBCCOPYKEY_API BOOL SetWBWarm(LPCTSTR data)
+{
+	if ( data == NULL)
+		return FALSE;
+	return g_objSiacp.SCBC_SetWBWarm(data);
+}
+
+SCBCCOPYKEY_API BOOL SetWBWarm(const byte* pBuffer, const int& nLen)
+{
+	return g_objSiacp.SCBC_SetWBWarm(pBuffer, nLen);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 检验;
+SCBCCOPYKEY_API BOOL CheckDeviceId()
+{
+	return g_objSiacp.SCBC_CheckDeviceId();
+}
+
+SCBCCOPYKEY_API BOOL CheckMAC()
+{
+	return g_objSiacp.SCBC_CheckMAC();
+}
+
+SCBCCOPYKEY_API BOOL CheckHDCP()
+{
+	return g_objSiacp.SCBC_CheckHDCP();
+}
+
+SCBCCOPYKEY_API BOOL CheckHDCP22()
+{
+	return g_objSiacp.SCBC_CheckHDCP22();
+}
+
+SCBCCOPYKEY_API BOOL CheckNetflixESN()
+{
+	return g_objSiacp.SCBC_CheckNetflixESN();
+}
+
+SCBCCOPYKEY_API BOOL CheckWidi()
+{
+	return g_objSiacp.SCBC_CheckWidi();
+}
+
+SCBCCOPYKEY_API BOOL CheckWidevine()
+{
+	return g_objSiacp.SCBC_CheckWidevine();
+}
+
+SCBCCOPYKEY_API BOOL CheckCikey()
+{
+	return g_objSiacp.SCBC_CheckCikey();
+}
+
+SCBCCOPYKEY_API BOOL StarWarmUpMode()
+{
+	return g_objSiacp.SCBC_StarWarmUpMode();
+}
+
+SCBCCOPYKEY_API BOOL StopWarmUpMode()
+{
+	return g_objSiacp.SCBC_StopWarmUpMode();
+}
+#else
+
+static PyObject* Open(PyObject* self, PyObject* args)
+{
+	int nPort;
+	int Baudrate;
+	int ByteSize, Parity, StopBits;
+	if (!PyArg_ParseTuple(args, "iiiii", &nPort, &Baudrate, &ByteSize, &Parity, &StopBits ))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.OpenComm(nPort, Baudrate));
+}
+
+static PyObject* Close(PyObject* self, PyObject* args)
+{
+	g_objSiacp.CloseComm();
+	return Py_None;
+}
+
+static PyObject* IsOpen(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.IsOpen());
+}
+
+static PyObject* EnterFactory(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_EnterFactory());
+}
+
+static PyObject* LeaveFactory(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_LeaveFactory());
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 读取;
+static PyObject* GetProjectId(PyObject* self, PyObject* args)
+{
+	int pid = -1;
+	g_objSiacp.SCBC_GetProjectId(pid);
+	// 获取失败返回-1;
+	return Py_BuildValue("i", pid);
+}
+
+static PyObject* GetSoftVersion(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetSoftVersion(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetDeviceId(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetDeviceId(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetClientType(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetClientType(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetMAC(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetMAC(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetHDCPKey(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetHDCPKey(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetHDCPKey22(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetHDCPKey22(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetWidi(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetWidi(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetNetflixESN(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetNetflixESN(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetWidevine(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetWidevine(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetCiKey(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetCiKey(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetOSDLanguage(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetOSDLanguage(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetShopLanguage(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetShopLanguage(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+static PyObject* GetChannel(PyObject* self, PyObject* args)
+{
+	g_strData.clear();
+	g_objSiacp.SCBC_GetChannel(g_strData);
+	// 获取失败返回"";
+	return Py_BuildValue("s", g_strData.c_str());
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 设置;
+static PyObject* SetProjectId(PyObject* self, PyObject* args)
+{
+	int pid = 0;
+	if (!PyArg_ParseTuple(args, "i", &pid))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetProjectId(pid));
+}
+
+static PyObject* SetDeviceId(PyObject* self, PyObject* args)
+{
+	const char* pszdata= 0;
+	if (!PyArg_ParseTuple(args, "s", &pszdata))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetDeviceId(pszdata));
+}
+
+static PyObject* SetDeviceIdBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetDeviceId((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetMAC(PyObject* self, PyObject* args)
+{
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszdata))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetMAC(pszdata));
+}
+
+static PyObject* SetMACBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetMAC((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetHDCPKey(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetHDCPKey(pszdata, bHasSpace));
+}
+
+static PyObject* SetHDCPKeyBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetHDCPKey((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetHDCPKey22(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetHDCPKey22(pszdata, bHasSpace));
+}
+
+static PyObject* SetHDCPKey22BF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetHDCPKey22((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetNetflixESN(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetNetflixESN(pszdata, bHasSpace));
+}
+
+static PyObject* SetNetflixESNBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetNetflixESN((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetWidi(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWidi(pszdata, bHasSpace));
+}
+
+static PyObject* SetWidiBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWidi((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetWidevine(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWidevine(pszdata, bHasSpace));
+}
+
+static PyObject* SetWidevineBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWidevine((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetCiKey(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetCiKey(pszdata, bHasSpace));
+}
+
+static PyObject* SetCiKeyBF(PyObject* self, PyObject* args)
+{
+	const char* pszfile = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszfile))
+		return NULL;
+
+	// 打开文件;
+	std::string data;
+	if ( ReadKeyFile(pszfile, data) )
+	{
+		return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetCiKey((BYTE*)data.c_str(), data.size()));
+	}
+
+	return Py_BuildValue("b", false);
+}
+
+static PyObject* SetOSDLanguage(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetOSDLanguage(pszdata, bHasSpace));
+}
+
+static PyObject* SetShopLanguage(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetShopLanguage(pszdata, bHasSpace));
+}
+
+static PyObject* SetChannel(PyObject* self, PyObject* args)
+{
+	BOOL bHasSpace; 
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "sb", &pszdata, &bHasSpace))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetChannel(pszdata, bHasSpace));
+}
+
+static PyObject* SetWBNormal(PyObject* self, PyObject* args)
+{
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszdata))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWBNormal(pszdata));
+}
+
+static PyObject* SetWBCool(PyObject* self, PyObject* args)
+{
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszdata))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWBCool(pszdata));
+}
+
+static PyObject* SetWBWarm(PyObject* self, PyObject* args)
+{
+	const char* pszdata = 0;
+	if (!PyArg_ParseTuple(args, "s", &pszdata))
+		return NULL;
+
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_SetWBWarm(pszdata));
+}
+//////////////////////////////////////////////////////////////////////////
+// 校验;
+static PyObject* CheckDeviceId(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckDeviceId());
+}
+
+static PyObject* CheckMAC(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckMAC());
+}
+
+static PyObject* CheckHDCP(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckHDCP());
+}
+
+static PyObject* CheckHDCP22(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckHDCP22());
+}
+
+static PyObject* CheckNetflixESN(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckNetflixESN());
+}
+
+static PyObject* CheckWidi(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckWidi());
+}
+
+static PyObject* CheckWidevine(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckWidevine());
+}
+
+static PyObject* CheckCikey(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_CheckCikey());
+}
+
+static PyObject* StarWarmUpMode(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_StarWarmUpMode());
+}
+
+static PyObject* StopWarmUpMode(PyObject* self, PyObject* args)
+{
+	return Py_BuildValue("b", (bool)g_objSiacp.SCBC_StopWarmUpMode());
+}
+
+// 描述方法,暴露给python的函数;
+static PyMethodDef ScbcCopyKey_Methods[] = {
+	{"Open",Open,METH_VARARGS,"打开串口"},
+	{"Close", Close, METH_VARARGS, "关闭串口"},
+	{"IsOpen", IsOpen, METH_VARARGS, "串口是否打开"},
+	{"EnterFactory", EnterFactory, METH_VARARGS, "进入工厂模式"},
+	{"LeaveFactory", LeaveFactory, METH_VARARGS, "离开工厂模式"},
+	//////////////////////////////////////////////////////////////////////////
+	// 读取;
+	{"GetProjectId", GetProjectId, METH_VARARGS, "获取pid"},
+	{"GetSoftVersion", GetSoftVersion, METH_VARARGS, "获取版本号"},
+	{"GetDeviceId", GetDeviceId, METH_VARARGS, "获取DeviceID"},
+	{"GetClientType", GetClientType, METH_VARARGS, "获取ClientType"},
+	{"GetMAC", GetMAC, METH_VARARGS, "获取MAC"},
+	{"GetHDCPKey", GetHDCPKey, METH_VARARGS, "获取HDCP"},
+	{"GetHDCPKey22", GetHDCPKey22, METH_VARARGS, "获取HDCP22"},
+	{"GetWidi", GetWidi, METH_VARARGS, "获取WIDI"},
+	{"GetNetflixESN", GetNetflixESN, METH_VARARGS, "获取ESN"},
+	{"GetWidevine", GetWidevine, METH_VARARGS, "获取WIDEVINE"},
+	{"GetCiKey", GetCiKey, METH_VARARGS, "获取CIKEY"},
+	{"GetOSDLanguage", GetOSDLanguage, METH_VARARGS, "获取OSDLAN"},
+	{"GetShopLanguage", GetShopLanguage, METH_VARARGS, "获取SHOPLAN"},
+	{"GetChannel", GetChannel, METH_VARARGS, "获取频道号"},
+	//////////////////////////////////////////////////////////////////////////
+	// 设置;
+	{"SetProjectId", SetProjectId, METH_VARARGS, "设置pid"},
+	{"SetDeviceId", SetDeviceId, METH_VARARGS, "设置DeviceId"},
+	{"SetDeviceIdBF", SetDeviceIdBF, METH_VARARGS, "设置DeviceId"},
+	{"SetMAC", SetMAC, METH_VARARGS, "设置MAC"},
+	{"SetMACBF", SetMACBF, METH_VARARGS, "设置MAC"},
+	{"SetHDCPKey", SetHDCPKey, METH_VARARGS, "设置HDCP"},
+	{"SetHDCPKeyBF", SetHDCPKeyBF, METH_VARARGS, "设置HDCP"},
+	{"SetHDCPKey22", SetHDCPKey22, METH_VARARGS, "设置HDCP22"},
+	{"SetHDCPKey22BF", SetHDCPKey22BF, METH_VARARGS, "设置HDCP22"},
+	{"SetWidi", SetWidi, METH_VARARGS, "设置WIDI"},
+	{"SetWidiBF", SetWidiBF, METH_VARARGS, "设置WIDI"},
+	{"SetNetflixESN", SetNetflixESN, METH_VARARGS, "设置ESN"},
+	{"SetNetflixESNBF", SetNetflixESNBF, METH_VARARGS, "设置ESN"},
+	{"SetWidevine", SetWidevine, METH_VARARGS, "设置WIDEVINE"},
+	{"SetWidevineBF", SetWidevineBF, METH_VARARGS, "设置WIDEVINE"},
+	{"SetCiKey", SetCiKey, METH_VARARGS, "设置CIKEY"},
+	{"SetCiKeyBF", SetCiKeyBF, METH_VARARGS, "设置CIKEY"},
+	{"SetOSDLanguage", SetOSDLanguage, METH_VARARGS, "设置OSDLAN"},
+	{"SetShopLanguage", SetShopLanguage, METH_VARARGS, "设置SHOPLAN"},
+	{"SetChannel", SetChannel, METH_VARARGS, "设置频道号"},
+
+	{"SetWBNormal", SetWBNormal, METH_VARARGS, "设置OSDLAN"},
+	{"SetWBCool", SetWBCool, METH_VARARGS, "设置SHOPLAN"},
+	{"SetWBWarm", SetWBWarm, METH_VARARGS, "设置频道号"},
+	//////////////////////////////////////////////////////////////////////////
+	// 校验;
+	{"CheckDeviceId", CheckDeviceId, METH_VARARGS, "检验pid"},
+	{"CheckMAC", CheckMAC, METH_VARARGS, "检验MAC"},
+	{"CheckHDCP", CheckHDCP, METH_VARARGS, "检验HDCP"},
+	{"CheckHDCP22", CheckHDCP22, METH_VARARGS, "检验HDCP22"},
+	{"CheckWidi", CheckWidi, METH_VARARGS, "检验WIDI"},
+	{"CheckNetflixESN", CheckNetflixESN, METH_VARARGS, "检验ESN"},
+	{"CheckWidevine", CheckWidevine, METH_VARARGS, "检验WIDEVINE"},
+	{"CheckCikey", CheckCikey, METH_VARARGS, "检验CIKEY"},
+	{"StarWarmUpMode", StarWarmUpMode, METH_VARARGS, "检验OSDLAN"},
+	{"StopWarmUpMode", StopWarmUpMode, METH_VARARGS, "检验SHOPLAN"},
+	{NULL,NULL}
+};
+
+// 初始模块;//格式:init<模块名称>
+PyMODINIT_FUNC initScbcCopyKey()
+{
+	// 初始化pyd函数列表;
+	PyObject* m, * d;
+	m = Py_InitModule("ScbcCopyKey", ScbcCopyKey_Methods);
+	d = PyModule_GetDict(m);
+}
+
+#endif

+ 97 - 0
ScbcCopyKey/ScbcCopyKey/ScbcCopyKey.h

@@ -0,0 +1,97 @@
+// 下列 ifdef 块是创建使从 DLL 导出更简单的
+// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 SCBCCOPYKEY_EXPORTS
+// 符号编译的。在使用此 DLL 的
+// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
+// SCBCCOPYKEY_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
+// 符号视为是被导出的。
+#ifdef SCBCCOPYKEY_EXPORTS
+#define SCBCCOPYKEY_API __declspec(dllexport)
+#else
+#define SCBCCOPYKEY_API __declspec(dllimport)
+#endif
+
+#ifndef __MAKE_PYD__
+// 打开串口;
+SCBCCOPYKEY_API BOOL Open(int nPort, DWORD dwBaudrate, BYTE ByteSize, BYTE Parity, BYTE StopBits);
+// 关闭串口;
+SCBCCOPYKEY_API void Close();
+// 串口是否打开;
+SCBCCOPYKEY_API BOOL IsOpen();
+// 进入工厂模式;
+SCBCCOPYKEY_API BOOL EnterFactory();
+// 离开工厂模式;
+SCBCCOPYKEY_API BOOL LeaveFactory();
+// 获取pid;
+SCBCCOPYKEY_API INT GetProjectId();
+// 软件版本号;
+SCBCCOPYKEY_API LPCTSTR GetSoftVersion();
+// 设备ID
+SCBCCOPYKEY_API LPCTSTR GetDeviceId();
+// ClientType;
+SCBCCOPYKEY_API LPCTSTR GetClientType();
+// MAC地址;
+SCBCCOPYKEY_API LPCTSTR GetMAC();
+// HDCP Key;
+SCBCCOPYKEY_API LPCTSTR GetHDCPKey();
+// HDCP Key2.2;
+SCBCCOPYKEY_API LPCTSTR GetHDCPKey22();
+// Widi;
+SCBCCOPYKEY_API LPCTSTR GetWidi();
+// Netflix ESN;
+SCBCCOPYKEY_API LPCTSTR GetNetflixESN();
+// Widevine;
+SCBCCOPYKEY_API LPCTSTR GetWidevine();
+// ci plus key;
+SCBCCOPYKEY_API LPCTSTR GetCiKey();
+// OSD Language;
+SCBCCOPYKEY_API LPCTSTR GetOSDLanguage();
+// Shop Language;
+SCBCCOPYKEY_API LPCTSTR GetShopLanguage();
+
+SCBCCOPYKEY_API LPCTSTR GetChannel();
+
+SCBCCOPYKEY_API BOOL SetProjectId(int pid);
+SCBCCOPYKEY_API BOOL SetProjectId(LPCTSTR lpPid);
+SCBCCOPYKEY_API BOOL SetProjectId(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetDeviceId(LPCTSTR lpDeviceId);
+SCBCCOPYKEY_API BOOL SetDeviceId(const byte* pBuffer, const int &nLen);
+SCBCCOPYKEY_API BOOL SetMAC(LPCTSTR lpMac);
+SCBCCOPYKEY_API BOOL SetMAC(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetHDCPKey(LPCTSTR lpHDCP, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetHDCPKey(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetHDCPKey22(LPCTSTR lpHDCP22, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetHDCPKey22(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetNetflixESN(LPCTSTR lpESN, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetNetflixESN(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetWidi(LPCTSTR lpWidi, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetWidi(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetWidevine(LPCTSTR lpWidevine, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetWidevine(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetCiKey(LPCTSTR lpCiKey, BOOL bHasSpace = FALSE);
+SCBCCOPYKEY_API BOOL SetCiKey(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetOSDLanguage(LPCTSTR lan, BOOL bHasSpace = TRUE);
+SCBCCOPYKEY_API BOOL SetOSDLanguage(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetShopLanguage(LPCTSTR lan, BOOL bHasSpace = TRUE);
+SCBCCOPYKEY_API BOOL SetShopLanguage(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetChannel(LPCTSTR channel, BOOL bHasSpace = TRUE);
+SCBCCOPYKEY_API BOOL SetChannel(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetWBNormal(LPCTSTR data);
+SCBCCOPYKEY_API BOOL SetWBNormal(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetWBCool(LPCTSTR data);
+SCBCCOPYKEY_API BOOL SetWBCool(const byte* pBuffer, const int& nLen);
+SCBCCOPYKEY_API BOOL SetWBWarm(LPCTSTR data);
+SCBCCOPYKEY_API BOOL SetWBWarm(const byte* pBuffer, const int& nLen);
+
+SCBCCOPYKEY_API BOOL CheckDeviceId();
+SCBCCOPYKEY_API BOOL CheckMAC();
+SCBCCOPYKEY_API BOOL CheckHDCP();
+SCBCCOPYKEY_API BOOL CheckHDCP22();
+SCBCCOPYKEY_API BOOL CheckNetflixESN();
+SCBCCOPYKEY_API BOOL CheckWidi();
+SCBCCOPYKEY_API BOOL CheckWidevine();
+SCBCCOPYKEY_API BOOL CheckCikey();
+
+SCBCCOPYKEY_API BOOL StarWarmUpMode();
+SCBCCOPYKEY_API BOOL StopWarmUpMode();
+
+#endif

+ 69 - 0
ScbcCopyKey/ScbcCopyKey/ScbcCopyKey.rc

@@ -0,0 +1,69 @@
+//Microsoft Visual C++ 生成的资源脚本。
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 2 资源生成。
+//
+#include "afxres.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
+LANGUAGE 4, 2
+#pragma code_page(936)
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 字符串表
+//
+
+STRINGTABLE
+BEGIN
+   IDS_APP_TITLE       "ScbcCopyKey"
+END
+
+#endif
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// 从 TEXTINCLUDE 3 资源生成。
+//
+#ifndef _AFXDLL
+#include "l.CHS\\afxres.rc"
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // 不是 APSTUDIO_INVOKED

+ 476 - 0
ScbcCopyKey/ScbcCopyKey/ScbcCopyKey.vcproj

@@ -0,0 +1,476 @@
+<?xml version="1.0" encoding="gb2312"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="ScbcCopyKey"
+	ProjectGUID="{AC652CEB-5047-4783-AA18-6B64AA4E78B5}"
+	RootNamespace="ScbcCopyKey"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SCBCCOPYKEY_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCBCCOPYKEY_EXPORTS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="ReleasePYD|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="C:\Python27\include"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCBCCOPYKEY_EXPORTS;__MAKE_PYD__"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="C:\Python27\libs"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine="copy $(TargetPath)  $(TargetDir)$(TargetName).pyd /y/a"
+			/>
+		</Configuration>
+		<Configuration
+			Name="DebugEXE|Win32"
+			OutputDirectory="..\..\..\..\bin\$(SolutionName)"
+			IntermediateDirectory="$(OutDir)\$(ProjectName)\$(ConfigurationName)"
+			ConfigurationType="1"
+			UseOfMFC="2"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__CONSOLE__;SCBCCOPYKEY_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Ô´Îļþ"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\Global.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\OTA.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ScbcCopyKey.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="ReleasePYD|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="DebugEXE|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\SynSerial.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\tinyxml2.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="ReleasePYD|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="DebugEXE|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="0"
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Í·Îļþ"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\CritSection.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Global.h"
+				>
+			</File>
+			<File
+				RelativePath=".\OTA.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Resource.h"
+				>
+			</File>
+			<File
+				RelativePath=".\ScbcCopyKey.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdint.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SynSerial.h"
+				>
+			</File>
+			<File
+				RelativePath=".\targetver.h"
+				>
+			</File>
+			<File
+				RelativePath=".\tinyxml2.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="×ÊÔ´Îļþ"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+			<File
+				RelativePath=".\ScbcCopyKey.rc"
+				>
+			</File>
+		</Filter>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 314 - 0
ScbcCopyKey/ScbcCopyKey/SynSerial.cpp

@@ -0,0 +1,314 @@
+#include "StdAfx.h"
+#include "SynSerial.h"
+
+CSynSerial::CSynSerial(void) :m_hSerialPort(NULL)
+, m_dwInQueue(1024)
+, m_dwOutQueue(1024)
+{
+	memset(&m_dcb, 0, sizeof(DCB));
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	memset(&m_szSerialPort, 0, sizeof(TCHAR)*MAX_PORT_LEN);
+}
+
+CSynSerial::~CSynSerial(void)
+{
+	CloseSerialPort();
+}
+
+BOOL CSynSerial::OpenSerialPort(IN CONST BYTE & byCommPort, IN CONST DWORD & dwBaudRate, IN CONST BYTE & bySize, IN CONST BYTE & byParity, IN CONST BYTE & byStopBits, IN CONST BYTE & byStartAddr, IN CONST INT & nInterval)
+{
+	ASSERT(byCommPort);
+	CloseSerialPort();
+
+	_stprintf_s(m_szSerialPort, _T("\\\\.\\com%d"), (int)byCommPort);
+	m_hSerialPort = CreateFile(m_szSerialPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
+	if (m_hSerialPort == INVALID_HANDLE_VALUE)
+	{
+		// 打开串口失败;
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	BOOL bResult = FALSE;
+	// SetupComm设置缓冲区大小;
+	bResult = SetupComm(m_hSerialPort, m_dwInQueue, m_dwOutQueue);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 在串口读写之前,清除缓冲区;
+	bResult = PurgeComm(m_hSerialPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// GetCommState获取设备控制块状态;
+	memset(&m_dcb, 0, sizeof(DCB));
+	bResult = GetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	m_dcb.Parity = byParity;
+	if (m_dcb.Parity == NOPARITY)
+		m_dcb.fParity = FALSE;
+	else
+		m_dcb.fParity = TRUE;
+
+	m_dcb.BaudRate = dwBaudRate;
+	m_dcb.ByteSize = bySize;
+	m_dcb.StopBits = byStopBits;
+	if (m_dcb.ByteSize == 8)
+		m_dcb.StopBits = ONESTOPBIT;
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 获取设备的超时值;
+	bResult = GetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 两字符之间最大的延时,设置0表示参数不起作用;
+	m_cts.ReadIntervalTimeout = 50;
+	m_cts.ReadTotalTimeoutMultiplier = 100;
+	m_cts.ReadTotalTimeoutConstant = 8000;
+	m_cts.WriteTotalTimeoutMultiplier = 100;
+	m_cts.WriteTotalTimeoutConstant = 5000;
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL CSynSerial::ReOpenSerialPort()
+{
+	ASSERT(_tcslen(m_szSerialPort));
+	CloseSerialPort();
+
+	m_hSerialPort = CreateFile(m_szSerialPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
+	if (m_hSerialPort == INVALID_HANDLE_VALUE)
+	{
+		// 打开串口失败;
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	BOOL bResult = FALSE;
+	// SetupComm设置缓冲区大小;
+	bResult = SetupComm(m_hSerialPort, m_dwInQueue, m_dwOutQueue);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 在串口读写之前,清除缓冲区;
+	bResult = PurgeComm(m_hSerialPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CSynSerial::CloseSerialPort()
+{
+	if (m_hSerialPort != NULL && m_hSerialPort != INVALID_HANDLE_VALUE)
+		CloseHandle(m_hSerialPort);
+	m_hSerialPort = NULL;
+}
+
+BOOL CSynSerial::SetSerialPort(IN CONST DWORD & dwBaudRate, IN CONST BYTE & byByteSize, IN CONST BYTE & byParity, IN CONST BYTE & byStopBits, IN CONST BYTE & byStartAddr, IN CONST DWORD & dwInQueue, IN CONST DWORD & dwOutQueue)
+{
+	if (!IsOpen())
+		return FALSE;
+
+	BOOL bResult = FALSE;
+	// GetCommState获取设备控制块状态;
+	memset(&m_dcb, 0, sizeof(DCB));
+	bResult = GetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	m_dcb.Parity = byParity;
+	if (m_dcb.Parity == NOPARITY)
+		m_dcb.fParity = FALSE;
+	else
+		m_dcb.fParity = TRUE;
+
+	m_dcb.BaudRate = dwBaudRate;
+	m_dcb.ByteSize = byByteSize;
+	m_dcb.StopBits = byStopBits;
+	if (m_dcb.ByteSize == 8)
+		m_dcb.StopBits = ONESTOPBIT;
+
+	// SetCommState设置设备的控制块状态;
+	memset(&m_cts, 0, sizeof(COMMTIMEOUTS));
+	bResult = SetCommState(m_hSerialPort, &m_dcb);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 获取设备的超时值;
+	bResult = GetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	// 两字符之间最大的延时,设置0表示参数不起作用;
+	m_cts.ReadIntervalTimeout = 50;
+	m_cts.ReadTotalTimeoutMultiplier = 100;
+	m_cts.ReadTotalTimeoutConstant = 8000;
+	m_cts.WriteTotalTimeoutMultiplier = 100;
+	m_cts.WriteTotalTimeoutConstant = 3000;
+
+	// 设置设备的超时值;
+	bResult = SetCommTimeouts(m_hSerialPort, &m_cts);
+	if (!bResult)
+	{
+		DWORD dwError = GetLastError();
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void CSynSerial::SetSerialDCB(IN CONST DCB & dcb)
+{
+}
+
+void CSynSerial::SetSerialCommTimeouts(IN CONST COMMTIMEOUTS & commTimeouts)
+{
+}
+
+DWORD CSynSerial::WriteComm(IN BYTE * pWirteBuf, IN CONST INT32 nWriteSize)
+{
+	if (!IsOpen())
+		return 0;
+
+	if (pWirteBuf == NULL || !::AfxIsValidAddress(pWirteBuf, nWriteSize, FALSE))
+	{
+		return 0;
+	}
+	
+	DWORD dwErrorFlags;
+	DWORD dwBytesWritten = 0;	// 实际写入的字节数;
+	// 写前, 清除错误;
+	COMSTAT ComStat;
+	ClearCommError(m_hSerialPort, &dwErrorFlags, &ComStat);	
+	// 写前,清空缓存区;
+	PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
+	if ( !WriteFile(m_hSerialPort, pWirteBuf, nWriteSize, &dwBytesWritten, NULL) )
+	{
+		DWORD dwError = GetLastError();
+	}	
+
+	// 返回写入字节数;
+	return dwBytesWritten;
+}
+
+DWORD CSynSerial::ReadComm(IN BYTE * pReadBuf, IN CONST DWORD & dwReadSize)
+{
+	if (!IsOpen())
+		return 0;
+
+	if (pReadBuf == NULL || !::AfxIsValidAddress(pReadBuf, dwReadSize, FALSE))
+	{
+		return 0;
+	}
+
+	DWORD dwBytesRead = 0;
+	DWORD dwErrorFlags = 0;
+	// ReadFile前,使用ClearCommError清除错误;
+	COMSTAT ComStat = {0};
+	DWORD dwLastLen = 0;
+	ULONGLONG ulTick = GetTickCount64();	
+	// 直到有数据为止,超时3秒;
+	while (true)
+	{// cbInQue表示输入缓冲区的字节数; 
+		Sleep(300);
+		if (GetTickCount64() - ulTick > 3000)
+			break;
+		ClearCommError(m_hSerialPort, &dwErrorFlags, &ComStat);
+		// 防止读一次没读完,再读一次;二次长度一样表示读完;
+		if (ComStat.cbInQue != 0 && dwLastLen == ComStat.cbInQue)
+			break;
+		dwLastLen = ComStat.cbInQue;
+	}
+
+	if (ComStat.cbInQue == 0)
+	{
+		// 串口超时,无数据返回;
+		return 0;
+	}
+
+	dwBytesRead = ComStat.cbInQue;
+	if (dwReadSize < dwBytesRead)
+	{
+		dwBytesRead = dwReadSize;
+	}
+
+	if (ReadFile(m_hSerialPort, pReadBuf, dwBytesRead, &dwBytesRead, NULL))
+	{
+		return (INT)dwBytesRead;
+	}
+
+	return dwBytesRead;
+}
+
+void CSynSerial::SetTimeouts(COMMTIMEOUTS& timeouts)
+{
+	ASSERT(IsOpen());
+	if (!SetCommTimeouts(m_hSerialPort, &timeouts))
+	{
+		DWORD dwLastError = GetLastError();
+	}
+}

+ 85 - 0
ScbcCopyKey/ScbcCopyKey/SynSerial.h

@@ -0,0 +1,85 @@
+/************************************************************************/
+/*  Copyright (C), 2016-2020, [Home], 保留所有权利;
+/*  模 块 名:同步串口模块;
+/*  描    述:;
+/*
+/*  版    本:[V];
+/*  作    者:[Home];
+/*  日    期:[12/20/2017];
+/*
+/*
+/*  注    意:;
+/*
+/*  修改记录:[Home];
+/*  修改日期:;
+/*  修改版本:;
+/*  修改内容:;
+/************************************************************************/
+
+#ifndef __SYN_SERIAL__
+#define __SYN_SERIAL__
+
+#pragma once
+
+#define MAX_PORT_LEN 10
+
+class CSynSerial
+{
+public:
+	CSynSerial(void);
+	virtual ~CSynSerial(void);
+
+private:
+	DCB		m_dcb;
+	HANDLE	m_hSerialPort;
+	COMMTIMEOUTS	m_cts;
+
+	// 串口号(1~255);
+	TCHAR	m_szSerialPort[MAX_PORT_LEN];
+	// 输入缓存大小(byte);
+	DWORD	m_dwInQueue;
+	// 输出缓存大小(byte);
+	DWORD	m_dwOutQueue;
+
+public:
+	inline BOOL IsOpen() const {
+		return (m_hSerialPort == NULL || m_hSerialPort == INVALID_HANDLE_VALUE) ? FALSE : TRUE;
+	}
+
+	// 打开串口;
+	BOOL OpenSerialPort(
+		IN CONST BYTE& byCommPort,		// 串口号;
+		IN CONST DWORD& dwBaudRate,		// 波特率;
+		IN CONST BYTE& bySize,			// 数据位;
+		IN CONST BYTE& byParity,		// 校验位;
+		IN CONST BYTE& byStopBits,		// 停止位;
+		IN CONST BYTE& byStartAddr,		// 起始地址;
+		IN CONST INT& nInterval			// 间隔时间;
+	);
+	// 重装打开串口;
+	BOOL ReOpenSerialPort();
+	// 关闭串口;
+	void CloseSerialPort();
+	// 设置串口参数;
+	BOOL SetSerialPort(
+		IN CONST DWORD& dwBaudRate,		// 波特率;
+		IN CONST BYTE& byByteSize,		// 数据位;
+		IN CONST BYTE& byParity,		// 校验位;
+		IN CONST BYTE& byStopBits,		// 停止位;
+		IN CONST BYTE& byStartAddr,		// 起始地址;
+		IN CONST DWORD& dwInQueue,		// 输入缓存;
+		IN CONST DWORD& dwOutQueue		// 输出缓存;
+		);
+
+	// 设置dcb参数;
+	void SetSerialDCB(IN CONST DCB &dcb);
+	// 设置commtimeouts参数;
+	void SetSerialCommTimeouts(IN CONST COMMTIMEOUTS &commTimeouts);
+
+	virtual DWORD WriteComm(IN BYTE *pWirteBuf, IN CONST INT32 nWriteSize);
+	virtual DWORD ReadComm(IN BYTE *pReadBuf, IN CONST DWORD& nReadSize);
+
+	void SetTimeouts(COMMTIMEOUTS& timeouts);
+};
+
+#endif // __SYN_SERIAL__

+ 8 - 0
ScbcCopyKey/ScbcCopyKey/stdafx.cpp

@@ -0,0 +1,8 @@
+// stdafx.cpp : 只包括标准包含文件的源文件
+// ScbcCopyKey.pch 将作为预编译头
+// stdafx.obj 将包含预编译类型信息
+
+#include "stdafx.h"
+
+// TODO: 在 STDAFX.H 中
+// 引用任何所需的附加头文件,而不是在此文件中引用

+ 34 - 0
ScbcCopyKey/ScbcCopyKey/stdafx.h

@@ -0,0 +1,34 @@
+// stdafx.h : 标准系统包含文件的包含文件,
+// 或是经常使用但不常更改的
+// 特定于项目的包含文件
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN             // 从 Windows 头中排除极少使用的资料
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC 核心组件和标准组件
+#include <afxext.h>         // MFC 扩展
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
+#endif
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                     // MFC 对 Windows 公共控件的支持
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <iostream>
+// Windows 头文件:
+#include <windows.h>
+#include "tinyxml2.h"
+#include "CritSection.h"
+#include "Global.h"
+
+// TODO: 在此处引用程序需要的其他头文件

+ 259 - 0
ScbcCopyKey/ScbcCopyKey/stdint.h

@@ -0,0 +1,259 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2013 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of the product nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#if _MSC_VER >= 2000 // [
+#include <stdint.h>
+#else // ] _MSC_VER >= 1600 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C //   [
+#  define INTMAX_C   INT64_C
+#endif // INTMAX_C    ]
+#ifndef UINTMAX_C //  [
+#  define UINTMAX_C  UINT64_C
+#endif // UINTMAX_C   ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]

+ 24 - 0
ScbcCopyKey/ScbcCopyKey/targetver.h

@@ -0,0 +1,24 @@
+#pragma once
+
+// 以下宏定义要求的最低平台。要求的最低平台
+// 是具有运行应用程序所需功能的 Windows、Internet Explorer 等产品的
+// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能,宏可以
+// 正常工作。
+
+// 如果必须要针对低于以下指定版本的平台,请修改下列定义。
+// 有关不同平台对应值的最新信息,请参考 MSDN。
+#ifndef WINVER                          // 指定要求的最低平台是 Windows Vista。
+#define WINVER 0x0600           // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINNT            // 指定要求的最低平台是 Windows Vista。
+#define _WIN32_WINNT 0x0600     // 将此值更改为相应的值,以适用于 Windows 的其他版本。
+#endif
+
+#ifndef _WIN32_WINDOWS          // 指定要求的最低平台是 Windows 98。
+#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值,以适用于 Windows Me 或更高版本。
+#endif
+
+#ifndef _WIN32_IE                       // 指定要求的最低平台是 Internet Explorer 7.0。
+#define _WIN32_IE 0x0700        // 将此值更改为相应的值,以适用于 IE 的其他版本。
+#endif

+ 2837 - 0
ScbcCopyKey/ScbcCopyKey/tinyxml2.cpp

@@ -0,0 +1,2837 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml2.h"
+
+#include <new>		// yes, this one new style header, is in the Android SDK.
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <stddef.h>
+#   include <stdarg.h>
+#else
+#   include <cstddef>
+#   include <cstdarg>
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
+	/*int _snprintf_s(
+	   char *buffer,
+	   size_t sizeOfBuffer,
+	   size_t count,
+	   const char *format [,
+		  argument] ...
+	);*/
+	static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
+	{
+		va_list va;
+		va_start( va, format );
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		va_end( va );
+		return result;
+	}
+
+	static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
+	{
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		return result;
+	}
+
+	#define TIXML_VSCPRINTF	_vscprintf
+	#define TIXML_SSCANF	sscanf_s
+#elif defined _MSC_VER
+	// Microsoft Visual Studio 2003 and earlier or WinCE
+	#define TIXML_SNPRINTF	_snprintf
+	#define TIXML_VSNPRINTF _vsnprintf
+	#define TIXML_SSCANF	sscanf
+	#if (_MSC_VER < 1400 ) && (!defined WINCE)
+		// Microsoft Visual Studio 2003 and not WinCE.
+		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
+	#else
+		// Microsoft Visual Studio 2003 and earlier or WinCE.
+		static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+		{
+			int len = 512;
+			for (;;) {
+				len = len*2;
+				char* str = new char[len]();
+				const int required = _vsnprintf(str, len, format, va);
+				delete[] str;
+				if ( required != -1 ) {
+					TIXMLASSERT( required >= 0 );
+					len = required;
+					break;
+				}
+			}
+			TIXMLASSERT( len >= 0 );
+			return len;
+		}
+	#endif
+#else
+	// GCC version 3 and higher
+	//#warning( "Using sn* functions." )
+	#define TIXML_SNPRINTF	snprintf
+	#define TIXML_VSNPRINTF	vsnprintf
+	static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+	{
+		int len = vsnprintf( 0, 0, format, va );
+		TIXMLASSERT( len >= 0 );
+		return len;
+	}
+	#define TIXML_SSCANF   sscanf
+#endif
+
+
+static const char LINE_FEED				= (char)0x0a;			// all line endings are normalized to LF
+static const char LF = LINE_FEED;
+static const char CARRIAGE_RETURN		= (char)0x0d;			// CR gets filtered out
+static const char CR = CARRIAGE_RETURN;
+static const char SINGLE_QUOTE			= '\'';
+static const char DOUBLE_QUOTE			= '\"';
+
+// Bunch of unicode info at:
+//		http://www.unicode.org/faq/utf_bom.html
+//	ef bb bf (Microsoft "lead bytes") - designates UTF-8
+
+static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+namespace tinyxml2
+{
+
+struct Entity {
+    const char* pattern;
+    int length;
+    char value;
+};
+
+static const int NUM_ENTITIES = 5;
+static const Entity entities[NUM_ENTITIES] = {
+    { "quot", 4,	DOUBLE_QUOTE },
+    { "amp", 3,		'&'  },
+    { "apos", 4,	SINGLE_QUOTE },
+    { "lt",	2, 		'<'	 },
+    { "gt",	2,		'>'	 }
+};
+
+
+StrPair::~StrPair()
+{
+    Reset();
+}
+
+
+void StrPair::TransferTo( StrPair* other )
+{
+    if ( this == other ) {
+        return;
+    }
+    // This in effect implements the assignment operator by "moving"
+    // ownership (as in auto_ptr).
+
+    TIXMLASSERT( other != 0 );
+    TIXMLASSERT( other->_flags == 0 );
+    TIXMLASSERT( other->_start == 0 );
+    TIXMLASSERT( other->_end == 0 );
+
+    other->Reset();
+
+    other->_flags = _flags;
+    other->_start = _start;
+    other->_end = _end;
+
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+
+void StrPair::Reset()
+{
+    if ( _flags & NEEDS_DELETE ) {
+        delete [] _start;
+    }
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+
+void StrPair::SetStr( const char* str, int flags )
+{
+    TIXMLASSERT( str );
+    Reset();
+    size_t len = strlen( str );
+    TIXMLASSERT( _start == 0 );
+    _start = new char[ len+1 ];
+    memcpy( _start, str, len+1 );
+    _end = _start + len;
+    _flags = flags | NEEDS_DELETE;
+}
+
+
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( endTag && *endTag );
+	TIXMLASSERT(curLineNumPtr);
+
+    char* start = p;
+    char  endChar = *endTag;
+    size_t length = strlen( endTag );
+
+    // Inner loop of text parsing.
+    while ( *p ) {
+        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+            Set( start, p, strFlags );
+            return p + length;
+        } else if (*p == '\n') {
+            ++(*curLineNumPtr);
+        }
+        ++p;
+        TIXMLASSERT( p );
+    }
+    return 0;
+}
+
+
+char* StrPair::ParseName( char* p )
+{
+    if ( !p || !(*p) ) {
+        return 0;
+    }
+    if ( !XMLUtil::IsNameStartChar( *p ) ) {
+        return 0;
+    }
+
+    char* const start = p;
+    ++p;
+    while ( *p && XMLUtil::IsNameChar( *p ) ) {
+        ++p;
+    }
+
+    Set( start, p, 0 );
+    return p;
+}
+
+
+void StrPair::CollapseWhitespace()
+{
+    // Adjusting _start would cause undefined behavior on delete[]
+    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
+    // Trim leading space.
+    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
+
+    if ( *_start ) {
+        const char* p = _start;	// the read pointer
+        char* q = _start;	// the write pointer
+
+        while( *p ) {
+            if ( XMLUtil::IsWhiteSpace( *p )) {
+                p = XMLUtil::SkipWhiteSpace( p, 0 );
+                if ( *p == 0 ) {
+                    break;    // don't write to q; this trims the trailing space.
+                }
+                *q = ' ';
+                ++q;
+            }
+            *q = *p;
+            ++q;
+            ++p;
+        }
+        *q = 0;
+    }
+}
+
+
+const char* StrPair::GetStr()
+{
+    TIXMLASSERT( _start );
+    TIXMLASSERT( _end );
+    if ( _flags & NEEDS_FLUSH ) {
+        *_end = 0;
+        _flags ^= NEEDS_FLUSH;
+
+        if ( _flags ) {
+            const char* p = _start;	// the read pointer
+            char* q = _start;	// the write pointer
+
+            while( p < _end ) {
+                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
+                    // CR-LF pair becomes LF
+                    // CR alone becomes LF
+                    // LF-CR becomes LF
+                    if ( *(p+1) == LF ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
+                    if ( *(p+1) == CR ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
+                    // Entities handled by tinyXML2:
+                    // - special entities in the entity table [in/out]
+                    // - numeric character reference [in]
+                    //   &#20013; or &#x4e2d;
+
+                    if ( *(p+1) == '#' ) {
+                        const int buflen = 10;
+                        char buf[buflen] = { 0 };
+                        int len = 0;
+                        char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
+                        if ( adjusted == 0 ) {
+                            *q = *p;
+                            ++p;
+                            ++q;
+                        }
+                        else {
+                            TIXMLASSERT( 0 <= len && len <= buflen );
+                            TIXMLASSERT( q + len <= adjusted );
+                            p = adjusted;
+                            memcpy( q, buf, len );
+                            q += len;
+                        }
+                    }
+                    else {
+                        bool entityFound = false;
+                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
+                            const Entity& entity = entities[i];
+                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
+                                    && *( p + entity.length + 1 ) == ';' ) {
+                                // Found an entity - convert.
+                                *q = entity.value;
+                                ++q;
+                                p += entity.length + 2;
+                                entityFound = true;
+                                break;
+                            }
+                        }
+                        if ( !entityFound ) {
+                            // fixme: treat as error?
+                            ++p;
+                            ++q;
+                        }
+                    }
+                }
+                else {
+                    *q = *p;
+                    ++p;
+                    ++q;
+                }
+            }
+            *q = 0;
+        }
+        // The loop below has plenty going on, and this
+        // is a less useful mode. Break it out.
+        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
+            CollapseWhitespace();
+        }
+        _flags = (_flags & NEEDS_DELETE);
+    }
+    TIXMLASSERT( _start );
+    return _start;
+}
+
+
+
+
+// --------- XMLUtil ----------- //
+
+const char* XMLUtil::writeBoolTrue  = "true";
+const char* XMLUtil::writeBoolFalse = "false";
+
+void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
+{
+	static const char* defTrue  = "true";
+	static const char* defFalse = "false";
+
+	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
+	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
+}
+
+
+const char* XMLUtil::ReadBOM( const char* p, bool* bom )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( bom );
+    *bom = false;
+    const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
+    // Check for BOM:
+    if (    *(pu+0) == TIXML_UTF_LEAD_0
+            && *(pu+1) == TIXML_UTF_LEAD_1
+            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
+        *bom = true;
+        p += 3;
+    }
+    TIXMLASSERT( p );
+    return p;
+}
+
+
+void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+    const unsigned long BYTE_MASK = 0xBF;
+    const unsigned long BYTE_MARK = 0x80;
+    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+    if (input < 0x80) {
+        *length = 1;
+    }
+    else if ( input < 0x800 ) {
+        *length = 2;
+    }
+    else if ( input < 0x10000 ) {
+        *length = 3;
+    }
+    else if ( input < 0x200000 ) {
+        *length = 4;
+    }
+    else {
+        *length = 0;    // This code won't convert this correctly anyway.
+        return;
+    }
+
+    output += *length;
+
+    // Scary scary fall throughs are annotated with carefully designed comments
+    // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
+    switch (*length) {
+        case 4:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 3:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 2:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+            //fall through
+        case 1:
+            --output;
+            *output = (char)(input | FIRST_BYTE_MARK[*length]);
+            break;
+        default:
+            TIXMLASSERT( false );
+    }
+}
+
+
+const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
+{
+    // Presume an entity, and pull it out.
+    *length = 0;
+
+    if ( *(p+1) == '#' && *(p+2) ) {
+        unsigned long ucs = 0;
+        TIXMLASSERT( sizeof( ucs ) >= 4 );
+        ptrdiff_t delta = 0;
+        unsigned mult = 1;
+        static const char SEMICOLON = ';';
+
+        if ( *(p+2) == 'x' ) {
+            // Hexadecimal.
+            const char* q = p+3;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != 'x' ) {
+                unsigned int digit = 0;
+
+                if ( *q >= '0' && *q <= '9' ) {
+                    digit = *q - '0';
+                }
+                else if ( *q >= 'a' && *q <= 'f' ) {
+                    digit = *q - 'a' + 10;
+                }
+                else if ( *q >= 'A' && *q <= 'F' ) {
+                    digit = *q - 'A' + 10;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( digit < 16 );
+                TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                const unsigned int digitScaled = mult * digit;
+                TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                ucs += digitScaled;
+                TIXMLASSERT( mult <= UINT_MAX / 16 );
+                mult *= 16;
+                --q;
+            }
+        }
+        else {
+            // Decimal.
+            const char* q = p+2;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != '#' ) {
+                if ( *q >= '0' && *q <= '9' ) {
+                    const unsigned int digit = *q - '0';
+                    TIXMLASSERT( digit < 10 );
+                    TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                    const unsigned int digitScaled = mult * digit;
+                    TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                    ucs += digitScaled;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( mult <= UINT_MAX / 10 );
+                mult *= 10;
+                --q;
+            }
+        }
+        // convert the UCS to UTF-8
+        ConvertUTF32ToUTF8( ucs, value, length );
+        return p + delta + 1;
+    }
+    return p+1;
+}
+
+
+void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
+}
+
+
+void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
+}
+
+
+void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
+}
+
+/*
+	ToStr() of a number is a very tricky topic.
+	https://github.com/leethomason/tinyxml2/issues/106
+*/
+void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
+}
+
+
+void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
+}
+
+
+void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
+{
+	// horrible syntax trick to make the compiler happy about %lld
+	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
+}
+
+
+bool XMLUtil::ToInt( const char* str, int* value )
+{
+    if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
+{
+    if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToBool( const char* str, bool* value )
+{
+    int ival = 0;
+    if ( ToInt( str, &ival )) {
+        *value = (ival==0) ? false : true;
+        return true;
+    }
+    if ( StringEqual( str, "true" ) ) {
+        *value = true;
+        return true;
+    }
+    else if ( StringEqual( str, "false" ) ) {
+        *value = false;
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToFloat( const char* str, float* value )
+{
+    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToDouble( const char* str, double* value )
+{
+    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToInt64(const char* str, int64_t* value)
+{
+	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
+	if (TIXML_SSCANF(str, "%lld", &v) == 1) {
+		*value = (int64_t)v;
+		return true;
+	}
+	return false;
+}
+
+
+char* XMLDocument::Identify( char* p, XMLNode** node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( p );
+    char* const start = p;
+    int const startLine = _parseCurLineNum;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    if( !*p ) {
+        *node = 0;
+        TIXMLASSERT( p );
+        return p;
+    }
+
+    // These strings define the matching patterns:
+    static const char* xmlHeader		= { "<?" };
+    static const char* commentHeader	= { "<!--" };
+    static const char* cdataHeader		= { "<![CDATA[" };
+    static const char* dtdHeader		= { "<!" };
+    static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
+
+    static const int xmlHeaderLen		= 2;
+    static const int commentHeaderLen	= 4;
+    static const int cdataHeaderLen		= 9;
+    static const int dtdHeaderLen		= 2;
+    static const int elementHeaderLen	= 1;
+
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
+    XMLNode* returnNode = 0;
+    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += xmlHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += commentHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
+        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode = text;
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += cdataHeaderLen;
+        text->SetCData( true );
+    }
+    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += dtdHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+        returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += elementHeaderLen;
+    }
+    else {
+        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
+        p = start;	// Back it up, all the text counts.
+        _parseCurLineNum = startLine;
+    }
+
+    TIXMLASSERT( returnNode );
+    TIXMLASSERT( p );
+    *node = returnNode;
+    return p;
+}
+
+
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLNode ----------- //
+
+XMLNode::XMLNode( XMLDocument* doc ) :
+    _document( doc ),
+    _parent( 0 ),
+    _value(),
+    _parseLineNum( 0 ),
+    _firstChild( 0 ), _lastChild( 0 ),
+    _prev( 0 ), _next( 0 ),
+	_userData( 0 ),
+    _memPool( 0 )
+{
+}
+
+
+XMLNode::~XMLNode()
+{
+    DeleteChildren();
+    if ( _parent ) {
+        _parent->Unlink( this );
+    }
+}
+
+const char* XMLNode::Value() const
+{
+    // Edge case: XMLDocuments don't have a Value. Return null.
+    if ( this->ToDocument() )
+        return 0;
+    return _value.GetStr();
+}
+
+void XMLNode::SetValue( const char* str, bool staticMem )
+{
+    if ( staticMem ) {
+        _value.SetInternedStr( str );
+    }
+    else {
+        _value.SetStr( str );
+    }
+}
+
+XMLNode* XMLNode::DeepClone(XMLDocument* target) const
+{
+	XMLNode* clone = this->ShallowClone(target);
+	if (!clone) return 0;
+
+	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
+		XMLNode* childClone = child->DeepClone(target);
+		TIXMLASSERT(childClone);
+		clone->InsertEndChild(childClone);
+	}
+	return clone;
+}
+
+void XMLNode::DeleteChildren()
+{
+    while( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        DeleteChild( _firstChild );
+    }
+    _firstChild = _lastChild = 0;
+}
+
+
+void XMLNode::Unlink( XMLNode* child )
+{
+    TIXMLASSERT( child );
+    TIXMLASSERT( child->_document == _document );
+    TIXMLASSERT( child->_parent == this );
+    if ( child == _firstChild ) {
+        _firstChild = _firstChild->_next;
+    }
+    if ( child == _lastChild ) {
+        _lastChild = _lastChild->_prev;
+    }
+
+    if ( child->_prev ) {
+        child->_prev->_next = child->_next;
+    }
+    if ( child->_next ) {
+        child->_next->_prev = child->_prev;
+    }
+	child->_next = 0;
+	child->_prev = 0;
+	child->_parent = 0;
+}
+
+
+void XMLNode::DeleteChild( XMLNode* node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( node->_document == _document );
+    TIXMLASSERT( node->_parent == this );
+    Unlink( node );
+	TIXMLASSERT(node->_prev == 0);
+	TIXMLASSERT(node->_next == 0);
+	TIXMLASSERT(node->_parent == 0);
+    DeleteNode( node );
+}
+
+
+XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _lastChild ) {
+        TIXMLASSERT( _firstChild );
+        TIXMLASSERT( _lastChild->_next == 0 );
+        _lastChild->_next = addThis;
+        addThis->_prev = _lastChild;
+        _lastChild = addThis;
+
+        addThis->_next = 0;
+    }
+    else {
+        TIXMLASSERT( _firstChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        TIXMLASSERT( _firstChild->_prev == 0 );
+
+        _firstChild->_prev = addThis;
+        addThis->_next = _firstChild;
+        _firstChild = addThis;
+
+        addThis->_prev = 0;
+    }
+    else {
+        TIXMLASSERT( _lastChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+
+    TIXMLASSERT( afterThis );
+
+    if ( afterThis->_parent != this ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    if ( afterThis == addThis ) {
+        // Current state: BeforeThis -> AddThis -> OneAfterAddThis
+        // Now AddThis must disappear from it's location and then
+        // reappear between BeforeThis and OneAfterAddThis.
+        // So just leave it where it is.
+        return addThis;
+    }
+
+    if ( afterThis->_next == 0 ) {
+        // The last node or the only node.
+        return InsertEndChild( addThis );
+    }
+    InsertChildPreamble( addThis );
+    addThis->_prev = afterThis;
+    addThis->_next = afterThis->_next;
+    afterThis->_next->_prev = addThis;
+    afterThis->_next = addThis;
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+
+
+const XMLElement* XMLNode::FirstChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _next; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _prev; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // This is a recursive method, but thinking about it "at the current level"
+    // it is a pretty simple flat list:
+    //		<foo/>
+    //		<!-- comment -->
+    //
+    // With a special case:
+    //		<foo>
+    //		</foo>
+    //		<!-- comment -->
+    //
+    // Where the closing element (/foo) *must* be the next thing after the opening
+    // element, and the names must match. BUT the tricky bit is that the closing
+    // element will be read by the child.
+    //
+    // 'endTag' is the end tag for this node, it is returned by a call to a child.
+    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
+
+	XMLDocument::DepthTracker tracker(_document);
+	if (_document->Error())
+		return 0;
+
+	while( p && *p ) {
+        XMLNode* node = 0;
+
+        p = _document->Identify( p, &node );
+        TIXMLASSERT( p );
+        if ( node == 0 ) {
+            break;
+        }
+
+        int initialLineNum = node->_parseLineNum;
+
+        StrPair endTag;
+        p = node->ParseDeep( p, &endTag, curLineNumPtr );
+        if ( !p ) {
+            DeleteNode( node );
+            if ( !_document->Error() ) {
+                _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
+            }
+            break;
+        }
+
+        XMLDeclaration* decl = node->ToDeclaration();
+        if ( decl ) {
+            // Declarations are only allowed at document level
+            //
+            // Multiple declarations are allowed but all declarations
+            // must occur before anything else. 
+            //
+            // Optimized due to a security test case. If the first node is 
+            // a declaration, and the last node is a declaration, then only 
+            // declarations have so far been addded.
+            bool wellLocated = false;
+
+            if (ToDocument()) {
+                if (FirstChild()) {
+                    wellLocated =
+                        FirstChild() &&
+                        FirstChild()->ToDeclaration() &&
+                        LastChild() &&
+                        LastChild()->ToDeclaration();
+                }
+                else {
+                    wellLocated = true;
+                }
+            }
+            if ( !wellLocated ) {
+                _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
+                DeleteNode( node );
+                break;
+            }
+        }
+
+        XMLElement* ele = node->ToElement();
+        if ( ele ) {
+            // We read the end tag. Return it to the parent.
+            if ( ele->ClosingType() == XMLElement::CLOSING ) {
+                if ( parentEndTag ) {
+                    ele->_value.TransferTo( parentEndTag );
+                }
+                node->_memPool->SetTracked();   // created and then immediately deleted.
+                DeleteNode( node );
+                return p;
+            }
+
+            // Handle an end tag returned to this level.
+            // And handle a bunch of annoying errors.
+            bool mismatch = false;
+            if ( endTag.Empty() ) {
+                if ( ele->ClosingType() == XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+            }
+            else {
+                if ( ele->ClosingType() != XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
+                    mismatch = true;
+                }
+            }
+            if ( mismatch ) {
+                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
+                DeleteNode( node );
+                break;
+            }
+        }
+        InsertEndChild( node );
+    }
+    return 0;
+}
+
+/*static*/ void XMLNode::DeleteNode( XMLNode* node )
+{
+    if ( node == 0 ) {
+        return;
+    }
+	TIXMLASSERT(node->_document);
+	if (!node->ToDocument()) {
+		node->_document->MarkInUse(node);
+	}
+
+    MemPool* pool = node->_memPool;
+    node->~XMLNode();
+    pool->Free( node );
+}
+
+void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
+{
+    TIXMLASSERT( insertThis );
+    TIXMLASSERT( insertThis->_document == _document );
+
+	if (insertThis->_parent) {
+        insertThis->_parent->Unlink( insertThis );
+	}
+	else {
+		insertThis->_document->MarkInUse(insertThis);
+        insertThis->_memPool->SetTracked();
+	}
+}
+
+const XMLElement* XMLNode::ToElementWithName( const char* name ) const
+{
+    const XMLElement* element = this->ToElement();
+    if ( element == 0 ) {
+        return 0;
+    }
+    if ( name == 0 ) {
+        return element;
+    }
+    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
+       return element;
+    }
+    return 0;
+}
+
+// --------- XMLText ---------- //
+char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    if ( this->CData() ) {
+        p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
+        }
+        return p;
+    }
+    else {
+        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
+        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
+            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
+        }
+
+        p = _value.ParseText( p, "<", flags, curLineNumPtr );
+        if ( p && *p ) {
+            return p-1;
+        }
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
+        }
+    }
+    return 0;
+}
+
+
+XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
+    text->SetCData( this->CData() );
+    return text;
+}
+
+
+bool XMLText::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLText* text = compare->ToText();
+    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
+}
+
+
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLComment ---------- //
+
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLComment::~XMLComment()
+{
+}
+
+
+char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Comment parses as text.
+    p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
+    return comment;
+}
+
+
+bool XMLComment::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLComment* comment = compare->ToComment();
+    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
+}
+
+
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLDeclaration ---------- //
+
+XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLDeclaration::~XMLDeclaration()
+{
+    //printf( "~XMLDeclaration\n" );
+}
+
+
+char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Declaration parses as text.
+    p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
+    return dec;
+}
+
+
+bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLDeclaration* declaration = compare->ToDeclaration();
+    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
+}
+
+
+
+bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLUnknown ---------- //
+
+XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLUnknown::~XMLUnknown()
+{
+}
+
+
+char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Unknown parses as text.
+    p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( !p ) {
+        _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
+    }
+    return p;
+}
+
+
+XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
+    return text;
+}
+
+
+bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLUnknown* unknown = compare->ToUnknown();
+    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
+}
+
+
+bool XMLUnknown::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLAttribute ---------- //
+
+const char* XMLAttribute::Name() const
+{
+    return _name.GetStr();
+}
+
+const char* XMLAttribute::Value() const
+{
+    return _value.GetStr();
+}
+
+char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
+{
+    // Parse using the name rules: bug fix, was using ParseText before
+    p = _name.ParseName( p );
+    if ( !p || !*p ) {
+        return 0;
+    }
+
+    // Skip white space before =
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '=' ) {
+        return 0;
+    }
+
+    ++p;	// move up to opening quote
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '\"' && *p != '\'' ) {
+        return 0;
+    }
+
+    char endTag[2] = { *p, 0 };
+    ++p;	// move past opening quote
+
+    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
+    return p;
+}
+
+
+void XMLAttribute::SetName( const char* n )
+{
+    _name.SetStr( n );
+}
+
+
+XMLError XMLAttribute::QueryIntValue( int* value ) const
+{
+    if ( XMLUtil::ToInt( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
+{
+    if ( XMLUtil::ToUnsigned( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
+{
+	if (XMLUtil::ToInt64(Value(), value)) {
+		return XML_SUCCESS;
+	}
+	return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryBoolValue( bool* value ) const
+{
+    if ( XMLUtil::ToBool( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryFloatValue( float* value ) const
+{
+    if ( XMLUtil::ToFloat( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryDoubleValue( double* value ) const
+{
+    if ( XMLUtil::ToDouble( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+void XMLAttribute::SetAttribute( const char* v )
+{
+    _value.SetStr( v );
+}
+
+
+void XMLAttribute::SetAttribute( int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	_value.SetStr(buf);
+}
+
+
+
+void XMLAttribute::SetAttribute( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( float v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+    _closingType( OPEN ),
+    _rootAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+    while( _rootAttribute ) {
+        XMLAttribute* next = _rootAttribute->_next;
+        DeleteAttribute( _rootAttribute );
+        _rootAttribute = next;
+    }
+}
+
+
+const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
+{
+    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
+        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+            return a;
+        }
+    }
+    return 0;
+}
+
+
+const char* XMLElement::Attribute( const char* name, const char* value ) const
+{
+    const XMLAttribute* a = FindAttribute( name );
+    if ( !a ) {
+        return 0;
+    }
+    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
+        return a->Value();
+    }
+    return 0;
+}
+
+int XMLElement::IntAttribute(const char* name, int defaultValue) const
+{
+	int i = defaultValue;
+	QueryIntAttribute(name, &i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const
+{
+	unsigned i = defaultValue;
+	QueryUnsignedAttribute(name, &i);
+	return i;
+}
+
+int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const
+{
+	int64_t i = defaultValue;
+	QueryInt64Attribute(name, &i);
+	return i;
+}
+
+bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const
+{
+	bool b = defaultValue;
+	QueryBoolAttribute(name, &b);
+	return b;
+}
+
+double XMLElement::DoubleAttribute(const char* name, double defaultValue) const
+{
+	double d = defaultValue;
+	QueryDoubleAttribute(name, &d);
+	return d;
+}
+
+float XMLElement::FloatAttribute(const char* name, float defaultValue) const
+{
+	float f = defaultValue;
+	QueryFloatAttribute(name, &f);
+	return f;
+}
+
+const char* XMLElement::GetText() const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        return FirstChild()->Value();
+    }
+    return "";
+}
+
+
+void	XMLElement::SetText( const char* inText )
+{
+	if ( FirstChild() && FirstChild()->ToText() )
+		FirstChild()->SetValue( inText );
+	else {
+		XMLText*	theText = GetDocument()->NewText( inText );
+		InsertFirstChild( theText );
+	}
+}
+
+
+void XMLElement::SetText( int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	SetText(buf);
+}
+
+
+void XMLElement::SetText( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( float v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+XMLError XMLElement::QueryIntText( int* ival ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToInt( t, ival ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToUnsigned( t, uval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryInt64Text(int64_t* ival) const
+{
+	if (FirstChild() && FirstChild()->ToText()) {
+		const char* t = FirstChild()->Value();
+		if (XMLUtil::ToInt64(t, ival)) {
+			return XML_SUCCESS;
+		}
+		return XML_CAN_NOT_CONVERT_TEXT;
+	}
+	return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryBoolText( bool* bval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToBool( t, bval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryDoubleText( double* dval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToDouble( t, dval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryFloatText( float* fval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToFloat( t, fval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+int XMLElement::IntText(int defaultValue) const
+{
+	int i = defaultValue;
+	QueryIntText(&i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedText(unsigned defaultValue) const
+{
+	unsigned i = defaultValue;
+	QueryUnsignedText(&i);
+	return i;
+}
+
+int64_t XMLElement::Int64Text(int64_t defaultValue) const
+{
+	int64_t i = defaultValue;
+	QueryInt64Text(&i);
+	return i;
+}
+
+bool XMLElement::BoolText(bool defaultValue) const
+{
+	bool b = defaultValue;
+	QueryBoolText(&b);
+	return b;
+}
+
+double XMLElement::DoubleText(double defaultValue) const
+{
+	double d = defaultValue;
+	QueryDoubleText(&d);
+	return d;
+}
+
+float XMLElement::FloatText(float defaultValue) const
+{
+	float f = defaultValue;
+	QueryFloatText(&f);
+	return f;
+}
+
+
+XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
+{
+    XMLAttribute* last = 0;
+    XMLAttribute* attrib = 0;
+    for( attrib = _rootAttribute;
+            attrib;
+            last = attrib, attrib = attrib->_next ) {
+        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+            break;
+        }
+    }
+    if ( !attrib ) {
+        attrib = CreateAttribute();
+        TIXMLASSERT( attrib );
+        if ( last ) {
+            TIXMLASSERT( last->_next == 0 );
+            last->_next = attrib;
+        }
+        else {
+            TIXMLASSERT( _rootAttribute == 0 );
+            _rootAttribute = attrib;
+        }
+        attrib->SetName( name );
+    }
+    return attrib;
+}
+
+
+void XMLElement::DeleteAttribute( const char* name )
+{
+    XMLAttribute* prev = 0;
+    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
+        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
+            if ( prev ) {
+                prev->_next = a->_next;
+            }
+            else {
+                _rootAttribute = a->_next;
+            }
+            DeleteAttribute( a );
+            break;
+        }
+        prev = a;
+    }
+}
+
+
+char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
+{
+    XMLAttribute* prevAttribute = 0;
+
+    // Read the attributes.
+    while( p ) {
+        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+        if ( !(*p) ) {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
+            return 0;
+        }
+
+        // attribute.
+        if (XMLUtil::IsNameStartChar( *p ) ) {
+            XMLAttribute* attrib = CreateAttribute();
+            TIXMLASSERT( attrib );
+            attrib->_parseLineNum = _document->_parseCurLineNum;
+
+            int attrLineNum = attrib->_parseLineNum;
+
+            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
+            if ( !p || Attribute( attrib->Name() ) ) {
+                DeleteAttribute( attrib );
+                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
+                return 0;
+            }
+            // There is a minor bug here: if the attribute in the source xml
+            // document is duplicated, it will not be detected and the
+            // attribute will be doubly added. However, tracking the 'prevAttribute'
+            // avoids re-scanning the attribute list. Preferring performance for
+            // now, may reconsider in the future.
+            if ( prevAttribute ) {
+                TIXMLASSERT( prevAttribute->_next == 0 );
+                prevAttribute->_next = attrib;
+            }
+            else {
+                TIXMLASSERT( _rootAttribute == 0 );
+                _rootAttribute = attrib;
+            }
+            prevAttribute = attrib;
+        }
+        // end of the tag
+        else if ( *p == '>' ) {
+            ++p;
+            break;
+        }
+        // end of the tag
+        else if ( *p == '/' && *(p+1) == '>' ) {
+            _closingType = CLOSED;
+            return p+2;	// done; sealed element.
+        }
+        else {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
+            return 0;
+        }
+    }
+    return p;
+}
+
+void XMLElement::DeleteAttribute( XMLAttribute* attribute )
+{
+    if ( attribute == 0 ) {
+        return;
+    }
+    MemPool* pool = attribute->_memPool;
+    attribute->~XMLAttribute();
+    pool->Free( attribute );
+}
+
+XMLAttribute* XMLElement::CreateAttribute()
+{
+    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
+    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+    TIXMLASSERT( attrib );
+    attrib->_memPool = &_document->_attributePool;
+    attrib->_memPool->SetTracked();
+    return attrib;
+}
+
+//
+//	<ele></ele>
+//	<ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // Read the element name.
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+
+    // The closing element is the </element> form. It is
+    // parsed just like a regular element then deleted from
+    // the DOM.
+    if ( *p == '/' ) {
+        _closingType = CLOSING;
+        ++p;
+    }
+
+    p = _value.ParseName( p );
+    if ( _value.Empty() ) {
+        return 0;
+    }
+
+    p = ParseAttributes( p, curLineNumPtr );
+    if ( !p || !*p || _closingType != OPEN ) {
+        return p;
+    }
+
+    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
+    return p;
+}
+
+
+
+XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
+    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
+        element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
+    }
+    return element;
+}
+
+
+bool XMLElement::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLElement* other = compare->ToElement();
+    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
+
+        const XMLAttribute* a=FirstAttribute();
+        const XMLAttribute* b=other->FirstAttribute();
+
+        while ( a && b ) {
+            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
+                return false;
+            }
+            a = a->Next();
+            b = b->Next();
+        }
+        if ( a || b ) {
+            // different count
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLDocument ----------- //
+
+// Warning: List must match 'enum XMLError'
+const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
+    "XML_SUCCESS",
+    "XML_NO_ATTRIBUTE",
+    "XML_WRONG_ATTRIBUTE_TYPE",
+    "XML_ERROR_FILE_NOT_FOUND",
+    "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
+    "XML_ERROR_FILE_READ_ERROR",
+    "XML_ERROR_PARSING_ELEMENT",
+    "XML_ERROR_PARSING_ATTRIBUTE",
+    "XML_ERROR_PARSING_TEXT",
+    "XML_ERROR_PARSING_CDATA",
+    "XML_ERROR_PARSING_COMMENT",
+    "XML_ERROR_PARSING_DECLARATION",
+    "XML_ERROR_PARSING_UNKNOWN",
+    "XML_ERROR_EMPTY_DOCUMENT",
+    "XML_ERROR_MISMATCHED_ELEMENT",
+    "XML_ERROR_PARSING",
+    "XML_CAN_NOT_CONVERT_TEXT",
+    "XML_NO_TEXT_NODE",
+	"XML_ELEMENT_DEPTH_EXCEEDED"
+};
+
+
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
+    XMLNode( 0 ),
+    _writeBOM( false ),
+    _processEntities( processEntities ),
+    _errorID(XML_SUCCESS),
+    _whitespaceMode( whitespaceMode ),
+    _errorStr(),
+    _errorLineNum( 0 ),
+    _charBuffer( 0 ),
+    _parseCurLineNum( 0 ),
+	_parsingDepth(0),
+    _unlinked(),
+    _elementPool(),
+    _attributePool(),
+    _textPool(),
+    _commentPool()
+{
+    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
+    _document = this;
+}
+
+
+XMLDocument::~XMLDocument()
+{
+    Clear();
+}
+
+
+void XMLDocument::MarkInUse(XMLNode* node)
+{
+	TIXMLASSERT(node);
+	TIXMLASSERT(node->_parent == 0);
+
+	for (int i = 0; i < _unlinked.Size(); ++i) {
+		if (node == _unlinked[i]) {
+			_unlinked.SwapRemove(i);
+			break;
+		}
+	}
+}
+
+void XMLDocument::Clear()
+{
+    DeleteChildren();
+	while( _unlinked.Size()) {
+		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
+	}
+
+#ifdef TINYXML2_DEBUG
+    const bool hadError = Error();
+#endif
+    ClearError();
+
+    delete [] _charBuffer;
+    _charBuffer = 0;
+	_parsingDepth = 0;
+
+#if 0
+    _textPool.Trace( "text" );
+    _elementPool.Trace( "element" );
+    _commentPool.Trace( "comment" );
+    _attributePool.Trace( "attribute" );
+#endif
+
+#ifdef TINYXML2_DEBUG
+    if ( !hadError ) {
+        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
+        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
+        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
+        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
+    }
+#endif
+}
+
+
+void XMLDocument::DeepCopy(XMLDocument* target) const
+{
+	TIXMLASSERT(target);
+    if (target == this) {
+        return; // technically success - a no-op.
+    }
+
+	target->Clear();
+	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
+		target->InsertEndChild(node->DeepClone(target));
+	}
+}
+
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
+    ele->SetName( name );
+    return ele;
+}
+
+
+XMLComment* XMLDocument::NewComment( const char* str )
+{
+    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
+    comment->SetValue( str );
+    return comment;
+}
+
+
+XMLText* XMLDocument::NewText( const char* str )
+{
+    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+    text->SetValue( str );
+    return text;
+}
+
+
+XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
+{
+    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
+    return dec;
+}
+
+
+XMLUnknown* XMLDocument::NewUnknown( const char* str )
+{
+    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+    unk->SetValue( str );
+    return unk;
+}
+
+static FILE* callfopen( const char* filepath, const char* mode )
+{
+    TIXMLASSERT( filepath );
+    TIXMLASSERT( mode );
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+    FILE* fp = 0;
+    errno_t err = fopen_s( &fp, filepath, mode );
+    if ( err ) {
+        return 0;
+    }
+#else
+    FILE* fp = fopen( filepath, mode );
+#endif
+    return fp;
+}
+
+void XMLDocument::DeleteNode( XMLNode* node )	{
+    TIXMLASSERT( node );
+    TIXMLASSERT(node->_document == this );
+    if (node->_parent) {
+        node->_parent->DeleteChild( node );
+    }
+    else {
+        // Isn't in the tree.
+        // Use the parent delete.
+        // Also, we need to mark it tracked: we 'know'
+        // it was never used.
+        node->_memPool->SetTracked();
+        // Call the static XMLNode version:
+        XMLNode::DeleteNode(node);
+    }
+}
+
+
+XMLError XMLDocument::LoadFile( const char* filename )
+{
+    if ( !filename ) {
+        TIXMLASSERT( false );
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
+        return _errorID;
+    }
+
+    Clear();
+    FILE* fp = callfopen( filename, "rb" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename );
+        return _errorID;
+    }
+    LoadFile( fp );
+    fclose( fp );
+    return _errorID;
+}
+
+// This is likely overengineered template art to have a check that unsigned long value incremented
+// by one still fits into size_t. If size_t type is larger than unsigned long type
+// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
+// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
+// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
+// types sizes relate to each other.
+template
+<bool = (sizeof(unsigned long) >= sizeof(size_t))>
+struct LongFitsIntoSizeTMinusOne {
+    static bool Fits( unsigned long value )
+    {
+        return value < (size_t)-1;
+    }
+};
+
+template <>
+struct LongFitsIntoSizeTMinusOne<false> {
+    static bool Fits( unsigned long )
+    {
+        return true;
+    }
+};
+
+XMLError XMLDocument::LoadFile( FILE* fp )
+{
+    Clear();
+
+    fseek( fp, 0, SEEK_SET );
+    if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    fseek( fp, 0, SEEK_END );
+    const long filelength = ftell( fp );
+    fseek( fp, 0, SEEK_SET );
+    if ( filelength == -1L ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+    TIXMLASSERT( filelength >= 0 );
+
+    if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
+        // Cannot handle files which won't fit in buffer together with null terminator
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    if ( filelength == 0 ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return _errorID;
+    }
+
+    const size_t size = filelength;
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[size+1];
+    size_t read = fread( _charBuffer, 1, size, fp );
+    if ( read != size ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
+        return _errorID;
+    }
+
+    _charBuffer[size] = 0;
+
+    Parse();
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( const char* filename, bool compact )
+{
+    if ( !filename ) {
+        TIXMLASSERT( false );
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=<null>" );
+        return _errorID;
+    }
+
+    FILE* fp = callfopen( filename, "w" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename );
+        return _errorID;
+    }
+    SaveFile(fp, compact);
+    fclose( fp );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
+{
+    // Clear any error from the last save, otherwise it will get reported
+    // for *this* call.
+    ClearError();
+    XMLPrinter stream( fp, compact );
+    Print( &stream );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::Parse( const char* p, size_t len )
+{
+    Clear();
+
+    if ( len == 0 || !p || !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return _errorID;
+    }
+    if ( len == (size_t)(-1) ) {
+        len = strlen( p );
+    }
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[ len+1 ];
+    memcpy( _charBuffer, p, len );
+    _charBuffer[len] = 0;
+
+    Parse();
+    if ( Error() ) {
+        // clean up now essentially dangling memory.
+        // and the parse fail can put objects in the
+        // pools that are dead and inaccessible.
+        DeleteChildren();
+        _elementPool.Clear();
+        _attributePool.Clear();
+        _textPool.Clear();
+        _commentPool.Clear();
+    }
+    return _errorID;
+}
+
+
+void XMLDocument::Print( XMLPrinter* streamer ) const
+{
+    if ( streamer ) {
+        Accept( streamer );
+    }
+    else {
+        XMLPrinter stdoutStreamer( stdout );
+        Accept( &stdoutStreamer );
+    }
+}
+
+
+void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
+{
+    TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
+    _errorID = error;
+    _errorLineNum = lineNum;
+	_errorStr.Reset();
+
+    size_t BUFFER_SIZE = 1000;
+    char* buffer = new char[BUFFER_SIZE];
+
+    TIXMLASSERT(sizeof(error) <= sizeof(int));
+    TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
+
+	if (format) {
+		size_t len = strlen(buffer);
+		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
+		len = strlen(buffer);
+
+		va_list va;
+		va_start(va, format);
+		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
+		va_end(va);
+	}
+	_errorStr.SetStr(buffer);
+	delete[] buffer;
+}
+
+
+/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
+{
+	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
+    const char* errorName = _errorNames[errorID];
+    TIXMLASSERT( errorName && errorName[0] );
+    return errorName;
+}
+
+const char* XMLDocument::ErrorStr() const
+{
+	return _errorStr.Empty() ? "" : _errorStr.GetStr();
+}
+
+
+void XMLDocument::PrintError() const
+{
+    printf("%s\n", ErrorStr());
+}
+
+const char* XMLDocument::ErrorName() const
+{
+    return ErrorIDToName(_errorID);
+}
+
+void XMLDocument::Parse()
+{
+    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
+    TIXMLASSERT( _charBuffer );
+    _parseCurLineNum = 1;
+    _parseLineNum = 1;
+    char* p = _charBuffer;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
+    if ( !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
+        return;
+    }
+    ParseDeep(p, 0, &_parseCurLineNum );
+}
+
+void XMLDocument::PushDepth()
+{
+	_parsingDepth++;
+	if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
+		SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." );
+	}
+}
+
+void XMLDocument::PopDepth()
+{
+	TIXMLASSERT(_parsingDepth > 0);
+	--_parsingDepth;
+}
+
+XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
+    _elementJustOpened( false ),
+    _stack(),
+    _firstElement( true ),
+    _fp( file ),
+    _depth( depth ),
+    _textDepth( -1 ),
+    _processEntities( true ),
+    _compactMode( compact ),
+    _buffer()
+{
+    for( int i=0; i<ENTITY_RANGE; ++i ) {
+        _entityFlag[i] = false;
+        _restrictedEntityFlag[i] = false;
+    }
+    for( int i=0; i<NUM_ENTITIES; ++i ) {
+        const char entityValue = entities[i].value;
+        const unsigned char flagIndex = (unsigned char)entityValue;
+        TIXMLASSERT( flagIndex < ENTITY_RANGE );
+        _entityFlag[flagIndex] = true;
+    }
+    _restrictedEntityFlag[(unsigned char)'&'] = true;
+    _restrictedEntityFlag[(unsigned char)'<'] = true;
+    _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
+    _buffer.Push( 0 );
+}
+
+
+void XMLPrinter::Print( const char* format, ... )
+{
+    va_list     va;
+    va_start( va, format );
+
+    if ( _fp ) {
+        vfprintf( _fp, format, va );
+    }
+    else {
+        const int len = TIXML_VSCPRINTF( format, va );
+        // Close out and re-start the va-args
+        va_end( va );
+        TIXMLASSERT( len >= 0 );
+        va_start( va, format );
+        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
+        char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
+		TIXML_VSNPRINTF( p, len+1, format, va );
+    }
+    va_end( va );
+}
+
+
+void XMLPrinter::Write( const char* data, size_t size )
+{
+    if ( _fp ) {
+        fwrite ( data , sizeof(char), size, _fp);
+    }
+    else {
+        char* p = _buffer.PushArr( static_cast<int>(size) ) - 1;   // back up over the null terminator.
+        memcpy( p, data, size );
+        p[size] = 0;
+    }
+}
+
+
+void XMLPrinter::Putc( char ch )
+{
+    if ( _fp ) {
+        fputc ( ch, _fp);
+    }
+    else {
+        char* p = _buffer.PushArr( sizeof(char) ) - 1;   // back up over the null terminator.
+        p[0] = ch;
+        p[1] = 0;
+    }
+}
+
+
+void XMLPrinter::PrintSpace( int depth )
+{
+    for( int i=0; i<depth; ++i ) {
+        Write( "    " );
+    }
+}
+
+
+void XMLPrinter::PrintString( const char* p, bool restricted )
+{
+    // Look for runs of bytes between entities to print.
+    const char* q = p;
+
+    if ( _processEntities ) {
+        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
+        while ( *q ) {
+            TIXMLASSERT( p <= q );
+            // Remember, char is sometimes signed. (How many times has that bitten me?)
+            if ( *q > 0 && *q < ENTITY_RANGE ) {
+                // Check for entities. If one is found, flush
+                // the stream up until the entity, write the
+                // entity, and keep looking.
+                if ( flag[(unsigned char)(*q)] ) {
+                    while ( p < q ) {
+                        const size_t delta = q - p;
+                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+                        Write( p, toPrint );
+                        p += toPrint;
+                    }
+                    bool entityPatternPrinted = false;
+                    for( int i=0; i<NUM_ENTITIES; ++i ) {
+                        if ( entities[i].value == *q ) {
+                            Putc( '&' );
+                            Write( entities[i].pattern, entities[i].length );
+                            Putc( ';' );
+                            entityPatternPrinted = true;
+                            break;
+                        }
+                    }
+                    if ( !entityPatternPrinted ) {
+                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
+                        TIXMLASSERT( false );
+                    }
+                    ++p;
+                }
+            }
+            ++q;
+            TIXMLASSERT( p <= q );
+        }
+        // Flush the remaining string. This will be the entire
+        // string if an entity wasn't found.
+        if ( p < q ) {
+            const size_t delta = q - p;
+            const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+            Write( p, toPrint );
+        }
+    }
+    else {
+        Write( p );
+    }
+}
+
+
+void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
+{
+    if ( writeBOM ) {
+        static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
+        Write( reinterpret_cast< const char* >( bom ) );
+    }
+    if ( writeDec ) {
+        PushDeclaration( "xml version=\"1.0\"" );
+    }
+}
+
+
+void XMLPrinter::OpenElement( const char* name, bool compactMode )
+{
+    SealElementIfJustOpened();
+    _stack.Push( name );
+
+    if ( _textDepth < 0 && !_firstElement && !compactMode ) {
+        Putc( '\n' );
+    }
+    if ( !compactMode ) {
+        PrintSpace( _depth );
+    }
+
+    Write ( "<" );
+    Write ( name );
+
+    _elementJustOpened = true;
+    _firstElement = false;
+    ++_depth;
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, const char* value )
+{
+    TIXMLASSERT( _elementJustOpened );
+    Putc ( ' ' );
+    Write( name );
+    Write( "=\"" );
+    PrintString( value, false );
+    Putc ( '\"' );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute(const char* name, int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	PushAttribute(name, buf);
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::CloseElement( bool compactMode )
+{
+    --_depth;
+    const char* name = _stack.Pop();
+
+    if ( _elementJustOpened ) {
+        Write( "/>" );
+    }
+    else {
+        if ( _textDepth < 0 && !compactMode) {
+            Putc( '\n' );
+            PrintSpace( _depth );
+        }
+        Write ( "</" );
+        Write ( name );
+        Write ( ">" );
+    }
+
+    if ( _textDepth == _depth ) {
+        _textDepth = -1;
+    }
+    if ( _depth == 0 && !compactMode) {
+        Putc( '\n' );
+    }
+    _elementJustOpened = false;
+}
+
+
+void XMLPrinter::SealElementIfJustOpened()
+{
+    if ( !_elementJustOpened ) {
+        return;
+    }
+    _elementJustOpened = false;
+    Putc( '>' );
+}
+
+
+void XMLPrinter::PushText( const char* text, bool cdata )
+{
+    _textDepth = _depth-1;
+
+    SealElementIfJustOpened();
+    if ( cdata ) {
+        Write( "<![CDATA[" );
+        Write( text );
+        Write( "]]>" );
+    }
+    else {
+        PrintString( text, true );
+    }
+}
+
+void XMLPrinter::PushText( int64_t value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+void XMLPrinter::PushText( int value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( unsigned value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( bool value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( float value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( double value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushComment( const char* comment )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<!--" );
+    Write( comment );
+    Write( "-->" );
+}
+
+
+void XMLPrinter::PushDeclaration( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<?" );
+    Write( value );
+    Write( "?>" );
+}
+
+
+void XMLPrinter::PushUnknown( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Putc( '\n' );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+
+    Write( "<!" );
+    Write( value );
+    Putc( '>' );
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLDocument& doc )
+{
+    _processEntities = doc.ProcessEntities();
+    if ( doc.HasBOM() ) {
+        PushHeader( true, false );
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
+{
+    const XMLElement* parentElem = 0;
+    if ( element.Parent() ) {
+        parentElem = element.Parent()->ToElement();
+    }
+    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
+    OpenElement( element.Name(), compactMode );
+    while ( attribute ) {
+        PushAttribute( attribute->Name(), attribute->Value() );
+        attribute = attribute->Next();
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitExit( const XMLElement& element )
+{
+    CloseElement( CompactMode(element) );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLText& text )
+{
+    PushText( text.Value(), text.CData() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLComment& comment )
+{
+    PushComment( comment.Value() );
+    return true;
+}
+
+bool XMLPrinter::Visit( const XMLDeclaration& declaration )
+{
+    PushDeclaration( declaration.Value() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLUnknown& unknown )
+{
+    PushUnknown( unknown.Value() );
+    return true;
+}
+
+}   // namespace tinyxml2

+ 2310 - 0
ScbcCopyKey/ScbcCopyKey/tinyxml2.h

@@ -0,0 +1,2310 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#ifndef TINYXML2_INCLUDED
+#define TINYXML2_INCLUDED
+
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <ctype.h>
+#   include <limits.h>
+#   include <stdio.h>
+#   include <stdlib.h>
+#   include <string.h>
+#	if defined(__PS3__)
+#		include <stddef.h>
+#	endif
+#else
+#   include <cctype>
+#   include <climits>
+#   include <cstdio>
+#   include <cstdlib>
+#   include <cstring>
+#endif
+
+#include "stdint.h"
+
+/*
+   TODO: intern strings instead of allocation.
+*/
+/*
+	gcc:
+        g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
+
+    Formatting, Artistic Style:
+        AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
+*/
+
+#if defined( _DEBUG ) || defined (__DEBUG__)
+#   ifndef TINYXML2_DEBUG
+#       define TINYXML2_DEBUG
+#   endif
+#endif
+
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable: 4251)
+#endif
+
+#ifdef _WIN32
+#   ifdef TINYXML2_EXPORT
+#       define TINYXML2_LIB __declspec(dllexport)
+#   elif defined(TINYXML2_IMPORT)
+#       define TINYXML2_LIB __declspec(dllimport)
+#   else
+#       define TINYXML2_LIB
+#   endif
+#elif __GNUC__ >= 4
+#   define TINYXML2_LIB __attribute__((visibility("default")))
+#else
+#   define TINYXML2_LIB
+#endif
+
+
+#if defined(TINYXML2_DEBUG)
+#   if defined(_MSC_VER)
+#       // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like
+#       define TIXMLASSERT( x )           if ( !((void)0,(x))) { __debugbreak(); }
+#   elif defined (ANDROID_NDK)
+#       include <android/log.h>
+#       define TIXMLASSERT( x )           if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
+#   else
+#       include <assert.h>
+#       define TIXMLASSERT                assert
+#   endif
+#else
+#   define TIXMLASSERT( x )               {}
+#endif
+
+
+/* Versioning, past 1.0.14:
+	http://semver.org/
+*/
+static const int TIXML2_MAJOR_VERSION = 7;
+static const int TIXML2_MINOR_VERSION = 0;
+static const int TIXML2_PATCH_VERSION = 1;
+
+#define TINYXML2_MAJOR_VERSION 7
+#define TINYXML2_MINOR_VERSION 0
+#define TINYXML2_PATCH_VERSION 1
+
+// A fixed element depth limit is problematic. There needs to be a
+// limit to avoid a stack overflow. However, that limit varies per
+// system, and the capacity of the stack. On the other hand, it's a trivial
+// attack that can result from ill, malicious, or even correctly formed XML,
+// so there needs to be a limit in place.
+static const int TINYXML2_MAX_ELEMENT_DEPTH = 100;
+
+namespace tinyxml2
+{
+class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLText;
+class XMLDeclaration;
+class XMLUnknown;
+class XMLPrinter;
+
+/*
+	A class that wraps strings. Normally stores the start and end
+	pointers into the XML file itself, and will apply normalization
+	and entity translation if actually read. Can also store (and memory
+	manage) a traditional char[]
+
+    Isn't clear why TINYXML2_LIB is needed; but seems to fix #719
+*/
+class TINYXML2_LIB StrPair
+{
+public:
+    enum {
+        NEEDS_ENTITY_PROCESSING			= 0x01,
+        NEEDS_NEWLINE_NORMALIZATION		= 0x02,
+        NEEDS_WHITESPACE_COLLAPSING     = 0x04,
+
+        TEXT_ELEMENT		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        TEXT_ELEMENT_LEAVE_ENTITIES		= NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_NAME		            = 0,
+        ATTRIBUTE_VALUE		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_VALUE_LEAVE_ENTITIES  = NEEDS_NEWLINE_NORMALIZATION,
+        COMMENT							= NEEDS_NEWLINE_NORMALIZATION
+    };
+
+    StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
+    ~StrPair();
+
+    void Set( char* start, char* end, int flags ) {
+        TIXMLASSERT( start );
+        TIXMLASSERT( end );
+        Reset();
+        _start  = start;
+        _end    = end;
+        _flags  = flags | NEEDS_FLUSH;
+    }
+
+    const char* GetStr();
+
+    bool Empty() const {
+        return _start == _end;
+    }
+
+    void SetInternedStr( const char* str ) {
+        Reset();
+        _start = const_cast<char*>(str);
+    }
+
+    void SetStr( const char* str, int flags=0 );
+
+    char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );
+    char* ParseName( char* in );
+
+    void TransferTo( StrPair* other );
+	void Reset();
+
+private:
+    void CollapseWhitespace();
+
+    enum {
+        NEEDS_FLUSH = 0x100,
+        NEEDS_DELETE = 0x200
+    };
+
+    int     _flags;
+    char*   _start;
+    char*   _end;
+
+    StrPair( const StrPair& other );	// not supported
+    void operator=( const StrPair& other );	// not supported, use TransferTo()
+};
+
+
+/*
+	A dynamic array of Plain Old Data. Doesn't support constructors, etc.
+	Has a small initial memory pool, so that low or no usage will not
+	cause a call to new/delete
+*/
+template <class T, int INITIAL_SIZE>
+class DynArray
+{
+public:
+    DynArray() :
+        _mem( _pool ),
+        _allocated( INITIAL_SIZE ),
+        _size( 0 )
+    {
+    }
+
+    ~DynArray() {
+        if ( _mem != _pool ) {
+            delete [] _mem;
+        }
+    }
+
+    void Clear() {
+        _size = 0;
+    }
+
+    void Push( T t ) {
+        TIXMLASSERT( _size < INT_MAX );
+        EnsureCapacity( _size+1 );
+        _mem[_size] = t;
+        ++_size;
+    }
+
+    T* PushArr( int count ) {
+        TIXMLASSERT( count >= 0 );
+        TIXMLASSERT( _size <= INT_MAX - count );
+        EnsureCapacity( _size+count );
+        T* ret = &_mem[_size];
+        _size += count;
+        return ret;
+    }
+
+    T Pop() {
+        TIXMLASSERT( _size > 0 );
+        --_size;
+        return _mem[_size];
+    }
+
+    void PopArr( int count ) {
+        TIXMLASSERT( _size >= count );
+        _size -= count;
+    }
+
+    bool Empty() const					{
+        return _size == 0;
+    }
+
+    T& operator[](int i)				{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& operator[](int i) const	{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& PeekTop() const            {
+        TIXMLASSERT( _size > 0 );
+        return _mem[ _size - 1];
+    }
+
+    int Size() const					{
+        TIXMLASSERT( _size >= 0 );
+        return _size;
+    }
+
+    int Capacity() const				{
+        TIXMLASSERT( _allocated >= INITIAL_SIZE );
+        return _allocated;
+    }
+
+	void SwapRemove(int i) {
+		TIXMLASSERT(i >= 0 && i < _size);
+		TIXMLASSERT(_size > 0);
+		_mem[i] = _mem[_size - 1];
+		--_size;
+	}
+
+    const T* Mem() const				{
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+    T* Mem() {
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+private:
+    DynArray( const DynArray& ); // not supported
+    void operator=( const DynArray& ); // not supported
+
+    void EnsureCapacity( int cap ) {
+        TIXMLASSERT( cap > 0 );
+        if ( cap > _allocated ) {
+            TIXMLASSERT( cap <= INT_MAX / 2 );
+            int newAllocated = cap * 2;
+            T* newMem = new T[newAllocated];
+            TIXMLASSERT( newAllocated >= _size );
+            memcpy( newMem, _mem, sizeof(T)*_size );	// warning: not using constructors, only works for PODs
+            if ( _mem != _pool ) {
+                delete [] _mem;
+            }
+            _mem = newMem;
+            _allocated = newAllocated;
+        }
+    }
+
+    T*  _mem;
+    T   _pool[INITIAL_SIZE];
+    int _allocated;		// objects allocated
+    int _size;			// number objects in use
+};
+
+
+/*
+	Parent virtual class of a pool for fast allocation
+	and deallocation of objects.
+*/
+class MemPool
+{
+public:
+    MemPool() {}
+    virtual ~MemPool() {}
+
+    virtual int ItemSize() const = 0;
+    virtual void* Alloc() = 0;
+    virtual void Free( void* ) = 0;
+    virtual void SetTracked() = 0;
+};
+
+
+/*
+	Template child class to create pools of the correct type.
+*/
+template< int ITEM_SIZE >
+class MemPoolT : public MemPool
+{
+public:
+    MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0)	{}
+    ~MemPoolT() {
+        MemPoolT< ITEM_SIZE >::Clear();
+    }
+
+    void Clear() {
+        // Delete the blocks.
+        while( !_blockPtrs.Empty()) {
+            Block* lastBlock = _blockPtrs.Pop();
+            delete lastBlock;
+        }
+        _root = 0;
+        _currentAllocs = 0;
+        _nAllocs = 0;
+        _maxAllocs = 0;
+        _nUntracked = 0;
+    }
+
+    virtual int ItemSize() const	{
+        return ITEM_SIZE;
+    }
+    int CurrentAllocs() const		{
+        return _currentAllocs;
+    }
+
+    virtual void* Alloc() {
+        if ( !_root ) {
+            // Need a new block.
+            Block* block = new Block();
+            _blockPtrs.Push( block );
+
+            Item* blockItems = block->items;
+            for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
+                blockItems[i].next = &(blockItems[i + 1]);
+            }
+            blockItems[ITEMS_PER_BLOCK - 1].next = 0;
+            _root = blockItems;
+        }
+        Item* const result = _root;
+        TIXMLASSERT( result != 0 );
+        _root = _root->next;
+
+        ++_currentAllocs;
+        if ( _currentAllocs > _maxAllocs ) {
+            _maxAllocs = _currentAllocs;
+        }
+        ++_nAllocs;
+        ++_nUntracked;
+        return result;
+    }
+
+    virtual void Free( void* mem ) {
+        if ( !mem ) {
+            return;
+        }
+        --_currentAllocs;
+        Item* item = static_cast<Item*>( mem );
+#ifdef TINYXML2_DEBUG
+        memset( item, 0xfe, sizeof( *item ) );
+#endif
+        item->next = _root;
+        _root = item;
+    }
+    void Trace( const char* name ) {
+        printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
+                name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
+                ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
+    }
+
+    void SetTracked() {
+        --_nUntracked;
+    }
+
+    int Untracked() const {
+        return _nUntracked;
+    }
+
+	// This number is perf sensitive. 4k seems like a good tradeoff on my machine.
+	// The test file is large, 170k.
+	// Release:		VS2010 gcc(no opt)
+	//		1k:		4000
+	//		2k:		4000
+	//		4k:		3900	21000
+	//		16k:	5200
+	//		32k:	4300
+	//		64k:	4000	21000
+    // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
+    // in private part if ITEMS_PER_BLOCK is private
+    enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };
+
+private:
+    MemPoolT( const MemPoolT& ); // not supported
+    void operator=( const MemPoolT& ); // not supported
+
+    union Item {
+        Item*   next;
+        char    itemData[ITEM_SIZE];
+    };
+    struct Block {
+        Item items[ITEMS_PER_BLOCK];
+    };
+    DynArray< Block*, 10 > _blockPtrs;
+    Item* _root;
+
+    int _currentAllocs;
+    int _nAllocs;
+    int _maxAllocs;
+    int _nUntracked;
+};
+
+
+
+/**
+	Implements the interface to the "Visitor pattern" (see the Accept() method.)
+	If you call the Accept() method, it requires being passed a XMLVisitor
+	class to handle callbacks. For nodes that contain other nodes (Document, Element)
+	you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
+	are simply called with Visit().
+
+	If you return 'true' from a Visit method, recursive parsing will continue. If you return
+	false, <b>no children of this node or its siblings</b> will be visited.
+
+	All flavors of Visit methods have a default implementation that returns 'true' (continue
+	visiting). You need to only override methods that are interesting to you.
+
+	Generally Accept() is called on the XMLDocument, although all nodes support visiting.
+
+	You should never change the document from a callback.
+
+	@sa XMLNode::Accept()
+*/
+class TINYXML2_LIB XMLVisitor
+{
+public:
+    virtual ~XMLVisitor() {}
+
+    /// Visit a document.
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+    /// Visit a document.
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    /// Visit an element.
+    virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )	{
+        return true;
+    }
+    /// Visit an element.
+    virtual bool VisitExit( const XMLElement& /*element*/ )			{
+        return true;
+    }
+
+    /// Visit a declaration.
+    virtual bool Visit( const XMLDeclaration& /*declaration*/ )		{
+        return true;
+    }
+    /// Visit a text node.
+    virtual bool Visit( const XMLText& /*text*/ )					{
+        return true;
+    }
+    /// Visit a comment node.
+    virtual bool Visit( const XMLComment& /*comment*/ )				{
+        return true;
+    }
+    /// Visit an unknown node.
+    virtual bool Visit( const XMLUnknown& /*unknown*/ )				{
+        return true;
+    }
+};
+
+// WARNING: must match XMLDocument::_errorNames[]
+enum XMLError {
+    XML_SUCCESS = 0,
+    XML_NO_ATTRIBUTE,
+    XML_WRONG_ATTRIBUTE_TYPE,
+    XML_ERROR_FILE_NOT_FOUND,
+    XML_ERROR_FILE_COULD_NOT_BE_OPENED,
+    XML_ERROR_FILE_READ_ERROR,
+    XML_ERROR_PARSING_ELEMENT,
+    XML_ERROR_PARSING_ATTRIBUTE,
+    XML_ERROR_PARSING_TEXT,
+    XML_ERROR_PARSING_CDATA,
+    XML_ERROR_PARSING_COMMENT,
+    XML_ERROR_PARSING_DECLARATION,
+    XML_ERROR_PARSING_UNKNOWN,
+    XML_ERROR_EMPTY_DOCUMENT,
+    XML_ERROR_MISMATCHED_ELEMENT,
+    XML_ERROR_PARSING,
+    XML_CAN_NOT_CONVERT_TEXT,
+    XML_NO_TEXT_NODE,
+	XML_ELEMENT_DEPTH_EXCEEDED,
+
+	XML_ERROR_COUNT
+};
+
+
+/*
+	Utility functionality.
+*/
+class TINYXML2_LIB XMLUtil
+{
+public:
+    static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr )	{
+        TIXMLASSERT( p );
+
+        while( IsWhiteSpace(*p) ) {
+            if (curLineNumPtr && *p == '\n') {
+                ++(*curLineNumPtr);
+            }
+            ++p;
+        }
+        TIXMLASSERT( p );
+        return p;
+    }
+    static char* SkipWhiteSpace( char* p, int* curLineNumPtr )				{
+        return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );
+    }
+
+    // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
+    // correct, but simple, and usually works.
+    static bool IsWhiteSpace( char p )					{
+        return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
+    }
+
+    inline static bool IsNameStartChar( unsigned char ch ) {
+        if ( ch >= 128 ) {
+            // This is a heuristic guess in attempt to not implement Unicode-aware isalpha()
+            return true;
+        }
+        if ( isalpha( ch ) ) {
+            return true;
+        }
+        return ch == ':' || ch == '_';
+    }
+
+    inline static bool IsNameChar( unsigned char ch ) {
+        return IsNameStartChar( ch )
+               || isdigit( ch )
+               || ch == '.'
+               || ch == '-';
+    }
+
+    inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
+        if ( p == q ) {
+            return true;
+        }
+        TIXMLASSERT( p );
+        TIXMLASSERT( q );
+        TIXMLASSERT( nChar >= 0 );
+        return strncmp( p, q, nChar ) == 0;
+    }
+
+    inline static bool IsUTF8Continuation( char p ) {
+        return ( p & 0x80 ) != 0;
+    }
+
+    static const char* ReadBOM( const char* p, bool* hasBOM );
+    // p is the starting location,
+    // the UTF-8 value of the entity will be placed in value, and length filled in.
+    static const char* GetCharacterRef( const char* p, char* value, int* length );
+    static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+    // converts primitive types to strings
+    static void ToStr( int v, char* buffer, int bufferSize );
+    static void ToStr( unsigned v, char* buffer, int bufferSize );
+    static void ToStr( bool v, char* buffer, int bufferSize );
+    static void ToStr( float v, char* buffer, int bufferSize );
+    static void ToStr( double v, char* buffer, int bufferSize );
+	static void ToStr(int64_t v, char* buffer, int bufferSize);
+
+    // converts strings to primitive types
+    static bool	ToInt( const char* str, int* value );
+    static bool ToUnsigned( const char* str, unsigned* value );
+    static bool	ToBool( const char* str, bool* value );
+    static bool	ToFloat( const char* str, float* value );
+    static bool ToDouble( const char* str, double* value );
+	static bool ToInt64(const char* str, int64_t* value);
+
+	// Changes what is serialized for a boolean value.
+	// Default to "true" and "false". Shouldn't be changed
+	// unless you have a special testing or compatibility need.
+	// Be careful: static, global, & not thread safe.
+	// Be sure to set static const memory as parameters.
+	static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);
+
+private:
+	static const char* writeBoolTrue;
+	static const char* writeBoolFalse;
+};
+
+
+/** XMLNode is a base class for every object that is in the
+	XML Document Object Model (DOM), except XMLAttributes.
+	Nodes have siblings, a parent, and children which can
+	be navigated. A node is always in a XMLDocument.
+	The type of a XMLNode can be queried, and it can
+	be cast to its more defined type.
+
+	A XMLDocument allocates memory for all its Nodes.
+	When the XMLDocument gets deleted, all its Nodes
+	will also be deleted.
+
+	@verbatim
+	A Document can contain:	Element	(container or leaf)
+							Comment (leaf)
+							Unknown (leaf)
+							Declaration( leaf )
+
+	An Element can contain:	Element (container or leaf)
+							Text	(leaf)
+							Attributes (not on tree)
+							Comment (leaf)
+							Unknown (leaf)
+
+	@endverbatim
+*/
+class TINYXML2_LIB XMLNode
+{
+    friend class XMLDocument;
+    friend class XMLElement;
+public:
+
+    /// Get the XMLDocument that owns this XMLNode.
+    const XMLDocument* GetDocument() const	{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+    /// Get the XMLDocument that owns this XMLNode.
+    XMLDocument* GetDocument()				{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+
+    /// Safely cast to an Element, or null.
+    virtual XMLElement*		ToElement()		{
+        return 0;
+    }
+    /// Safely cast to Text, or null.
+    virtual XMLText*		ToText()		{
+        return 0;
+    }
+    /// Safely cast to a Comment, or null.
+    virtual XMLComment*		ToComment()		{
+        return 0;
+    }
+    /// Safely cast to a Document, or null.
+    virtual XMLDocument*	ToDocument()	{
+        return 0;
+    }
+    /// Safely cast to a Declaration, or null.
+    virtual XMLDeclaration*	ToDeclaration()	{
+        return 0;
+    }
+    /// Safely cast to an Unknown, or null.
+    virtual XMLUnknown*		ToUnknown()		{
+        return 0;
+    }
+
+    virtual const XMLElement*		ToElement() const		{
+        return 0;
+    }
+    virtual const XMLText*			ToText() const			{
+        return 0;
+    }
+    virtual const XMLComment*		ToComment() const		{
+        return 0;
+    }
+    virtual const XMLDocument*		ToDocument() const		{
+        return 0;
+    }
+    virtual const XMLDeclaration*	ToDeclaration() const	{
+        return 0;
+    }
+    virtual const XMLUnknown*		ToUnknown() const		{
+        return 0;
+    }
+
+    /** The meaning of 'value' changes for the specific type.
+    	@verbatim
+    	Document:	empty (NULL is returned, not an empty string)
+    	Element:	name of the element
+    	Comment:	the comment text
+    	Unknown:	the tag contents
+    	Text:		the text string
+    	@endverbatim
+    */
+    const char* Value() const;
+
+    /** Set the Value of an XML node.
+    	@sa Value()
+    */
+    void SetValue( const char* val, bool staticMem=false );
+
+    /// Gets the line number the node is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// Get the parent of this node on the DOM.
+    const XMLNode*	Parent() const			{
+        return _parent;
+    }
+
+    XMLNode* Parent()						{
+        return _parent;
+    }
+
+    /// Returns true if this node has no children.
+    bool NoChildren() const					{
+        return !_firstChild;
+    }
+
+    /// Get the first child node, or null if none exists.
+    const XMLNode*  FirstChild() const		{
+        return _firstChild;
+    }
+
+    XMLNode*		FirstChild()			{
+        return _firstChild;
+    }
+
+    /** Get the first child element, or optionally the first child
+        element with the specified name.
+    */
+    const XMLElement* FirstChildElement( const char* name = 0 ) const;
+
+    XMLElement* FirstChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( name ));
+    }
+
+    /// Get the last child node, or null if none exists.
+    const XMLNode*	LastChild() const						{
+        return _lastChild;
+    }
+
+    XMLNode*		LastChild()								{
+        return _lastChild;
+    }
+
+    /** Get the last child element or optionally the last child
+        element with the specified name.
+    */
+    const XMLElement* LastChildElement( const char* name = 0 ) const;
+
+    XMLElement* LastChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name) );
+    }
+
+    /// Get the previous (left) sibling node of this node.
+    const XMLNode*	PreviousSibling() const					{
+        return _prev;
+    }
+
+    XMLNode*	PreviousSibling()							{
+        return _prev;
+    }
+
+    /// Get the previous (left) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	PreviousSiblingElement( const char* name = 0 ) const ;
+
+    XMLElement*	PreviousSiblingElement( const char* name = 0 ) {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( name ) );
+    }
+
+    /// Get the next (right) sibling node of this node.
+    const XMLNode*	NextSibling() const						{
+        return _next;
+    }
+
+    XMLNode*	NextSibling()								{
+        return _next;
+    }
+
+    /// Get the next (right) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	NextSiblingElement( const char* name = 0 ) const;
+
+    XMLElement*	NextSiblingElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( name ) );
+    }
+
+    /**
+    	Add a child node as the last (right) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertEndChild( XMLNode* addThis );
+
+    XMLNode* LinkEndChild( XMLNode* addThis )	{
+        return InsertEndChild( addThis );
+    }
+    /**
+    	Add a child node as the first (left) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertFirstChild( XMLNode* addThis );
+    /**
+    	Add a node after the specified child node.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the afterThis node
+		is not a child of this node, or if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+
+    /**
+    	Delete all the children of this node.
+    */
+    void DeleteChildren();
+
+    /**
+    	Delete a child of this node.
+    */
+    void DeleteChild( XMLNode* node );
+
+    /**
+    	Make a copy of this node, but not its children.
+    	You may pass in a Document pointer that will be
+    	the owner of the new Node. If the 'document' is
+    	null, then the node returned will be allocated
+    	from the current Document. (this->GetDocument())
+
+    	Note: if called on a XMLDocument, this will return null.
+    */
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
+
+	/**
+		Make a copy of this node and all its children.
+
+		If the 'target' is null, then the nodes will
+		be allocated in the current document. If 'target'
+        is specified, the memory will be allocated is the
+        specified XMLDocument.
+
+		NOTE: This is probably not the correct tool to
+		copy a document, since XMLDocuments can have multiple
+		top level XMLNodes. You probably want to use
+        XMLDocument::DeepCopy()
+	*/
+	XMLNode* DeepClone( XMLDocument* target ) const;
+
+    /**
+    	Test if 2 nodes are the same, but don't test children.
+    	The 2 nodes do not need to be in the same Document.
+
+    	Note: if called on a XMLDocument, this will return false.
+    */
+    virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
+
+    /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the
+    	XML tree will be conditionally visited and the host will be called back
+    	via the XMLVisitor interface.
+
+    	This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse
+    	the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this
+    	interface versus any other.)
+
+    	The interface has been based on ideas from:
+
+    	- http://www.saxproject.org/
+    	- http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+    	Which are both good references for "visiting".
+
+    	An example of using Accept():
+    	@verbatim
+    	XMLPrinter printer;
+    	tinyxmlDoc.Accept( &printer );
+    	const char* xmlcstr = printer.CStr();
+    	@endverbatim
+    */
+    virtual bool Accept( XMLVisitor* visitor ) const = 0;
+
+	/**
+		Set user data into the XMLNode. TinyXML-2 in
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void SetUserData(void* userData)	{ _userData = userData; }
+
+	/**
+		Get user data set into the XMLNode. TinyXML-2 in
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void* GetUserData() const			{ return _userData; }
+
+protected:
+    explicit XMLNode( XMLDocument* );
+    virtual ~XMLNode();
+
+    virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+    XMLDocument*	_document;
+    XMLNode*		_parent;
+    mutable StrPair	_value;
+    int             _parseLineNum;
+
+    XMLNode*		_firstChild;
+    XMLNode*		_lastChild;
+
+    XMLNode*		_prev;
+    XMLNode*		_next;
+
+	void*			_userData;
+
+private:
+    MemPool*		_memPool;
+    void Unlink( XMLNode* child );
+    static void DeleteNode( XMLNode* node );
+    void InsertChildPreamble( XMLNode* insertThis ) const;
+    const XMLElement* ToElementWithName( const char* name ) const;
+
+    XMLNode( const XMLNode& );	// not supported
+    XMLNode& operator=( const XMLNode& );	// not supported
+};
+
+
+/** XML text.
+
+	Note that a text node can have child element nodes, for example:
+	@verbatim
+	<root>This is <b>bold</b></root>
+	@endverbatim
+
+	A text node can have 2 ways to output the next. "normal" output
+	and CDATA. It will default to the mode it was parsed from the XML file and
+	you generally want to leave it alone, but you can change the output mode with
+	SetCData() and query it with CData().
+*/
+class TINYXML2_LIB XMLText : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLText* ToText()			{
+        return this;
+    }
+    virtual const XMLText* ToText() const	{
+        return this;
+    }
+
+    /// Declare whether this should be CDATA or standard text.
+    void SetCData( bool isCData )			{
+        _isCData = isCData;
+    }
+    /// Returns true if this is a CDATA text element.
+    bool CData() const						{
+        return _isCData;
+    }
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLText( XMLDocument* doc )	: XMLNode( doc ), _isCData( false )	{}
+    virtual ~XMLText()												{}
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    bool _isCData;
+
+    XMLText( const XMLText& );	// not supported
+    XMLText& operator=( const XMLText& );	// not supported
+};
+
+
+/** An XML Comment. */
+class TINYXML2_LIB XMLComment : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLComment*	ToComment()					{
+        return this;
+    }
+    virtual const XMLComment* ToComment() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLComment( XMLDocument* doc );
+    virtual ~XMLComment();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+private:
+    XMLComment( const XMLComment& );	// not supported
+    XMLComment& operator=( const XMLComment& );	// not supported
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+	@verbatim
+		<?xml version="1.0" standalone="yes"?>
+	@endverbatim
+
+	TinyXML-2 will happily read or write files without a declaration,
+	however.
+
+	The text of the declaration isn't interpreted. It is parsed
+	and written as a string.
+*/
+class TINYXML2_LIB XMLDeclaration : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLDeclaration*	ToDeclaration()					{
+        return this;
+    }
+    virtual const XMLDeclaration* ToDeclaration() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLDeclaration( XMLDocument* doc );
+    virtual ~XMLDeclaration();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLDeclaration( const XMLDeclaration& );	// not supported
+    XMLDeclaration& operator=( const XMLDeclaration& );	// not supported
+};
+
+
+/** Any tag that TinyXML-2 doesn't recognize is saved as an
+	unknown. It is a tag of text, but should not be modified.
+	It will be written back to the XML, unchanged, when the file
+	is saved.
+
+	DTD tags get thrown into XMLUnknowns.
+*/
+class TINYXML2_LIB XMLUnknown : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLUnknown*	ToUnknown()					{
+        return this;
+    }
+    virtual const XMLUnknown* ToUnknown() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    explicit XMLUnknown( XMLDocument* doc );
+    virtual ~XMLUnknown();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLUnknown( const XMLUnknown& );	// not supported
+    XMLUnknown& operator=( const XMLUnknown& );	// not supported
+};
+
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+	number of attributes, each with a unique name.
+
+	@note The attributes are not XMLNodes. You may only query the
+	Next() attribute in a list.
+*/
+class TINYXML2_LIB XMLAttribute
+{
+    friend class XMLElement;
+public:
+    /// The name of the attribute.
+    const char* Name() const;
+
+    /// The value of the attribute.
+    const char* Value() const;
+
+    /// Gets the line number the attribute is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// The next attribute in the list.
+    const XMLAttribute* Next() const {
+        return _next;
+    }
+
+    /** IntValue interprets the attribute as an integer, and returns the value.
+        If the value isn't an integer, 0 will be returned. There is no error checking;
+    	use QueryIntValue() if you need error checking.
+    */
+	int	IntValue() const {
+		int i = 0;
+		QueryIntValue(&i);
+		return i;
+	}
+
+	int64_t Int64Value() const {
+		int64_t i = 0;
+		QueryInt64Value(&i);
+		return i;
+	}
+
+    /// Query as an unsigned integer. See IntValue()
+    unsigned UnsignedValue() const			{
+        unsigned i=0;
+        QueryUnsignedValue( &i );
+        return i;
+    }
+    /// Query as a boolean. See IntValue()
+    bool	 BoolValue() const				{
+        bool b=false;
+        QueryBoolValue( &b );
+        return b;
+    }
+    /// Query as a double. See IntValue()
+    double 	 DoubleValue() const			{
+        double d=0;
+        QueryDoubleValue( &d );
+        return d;
+    }
+    /// Query as a float. See IntValue()
+    float	 FloatValue() const				{
+        float f=0;
+        QueryFloatValue( &f );
+        return f;
+    }
+
+    /** QueryIntValue interprets the attribute as an integer, and returns the value
+    	in the provided parameter. The function will return XML_SUCCESS on success,
+    	and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+    */
+    XMLError QueryIntValue( int* value ) const;
+    /// See QueryIntValue
+    XMLError QueryUnsignedValue( unsigned int* value ) const;
+	/// See QueryIntValue
+	XMLError QueryInt64Value(int64_t* value) const;
+	/// See QueryIntValue
+    XMLError QueryBoolValue( bool* value ) const;
+    /// See QueryIntValue
+    XMLError QueryDoubleValue( double* value ) const;
+    /// See QueryIntValue
+    XMLError QueryFloatValue( float* value ) const;
+
+    /// Set the attribute to a string value.
+    void SetAttribute( const char* value );
+    /// Set the attribute to value.
+    void SetAttribute( int value );
+    /// Set the attribute to value.
+    void SetAttribute( unsigned value );
+	/// Set the attribute to value.
+	void SetAttribute(int64_t value);
+	/// Set the attribute to value.
+    void SetAttribute( bool value );
+    /// Set the attribute to value.
+    void SetAttribute( double value );
+    /// Set the attribute to value.
+    void SetAttribute( float value );
+
+private:
+    enum { BUF_SIZE = 200 };
+
+    XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
+    virtual ~XMLAttribute()	{}
+
+    XMLAttribute( const XMLAttribute& );	// not supported
+    void operator=( const XMLAttribute& );	// not supported
+    void SetName( const char* name );
+
+    char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
+
+    mutable StrPair _name;
+    mutable StrPair _value;
+    int             _parseLineNum;
+    XMLAttribute*   _next;
+    MemPool*        _memPool;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+	and can contain other elements, text, comments, and unknowns.
+	Elements also contain an arbitrary number of attributes.
+*/
+class TINYXML2_LIB XMLElement : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    /// Get the name of an element (which is the Value() of the node.)
+    const char* Name() const		{
+        return Value();
+    }
+    /// Set the name of the element.
+    void SetName( const char* str, bool staticMem=false )	{
+        SetValue( str, staticMem );
+    }
+
+    virtual XMLElement* ToElement()				{
+        return this;
+    }
+    virtual const XMLElement* ToElement() const {
+        return this;
+    }
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /** Given an attribute name, Attribute() returns the value
+    	for the attribute of that name, or null if none
+    	exists. For example:
+
+    	@verbatim
+    	const char* value = ele->Attribute( "foo" );
+    	@endverbatim
+
+    	The 'value' parameter is normally null. However, if specified,
+    	the attribute will only be returned if the 'name' and 'value'
+    	match. This allow you to write code:
+
+    	@verbatim
+    	if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
+    	@endverbatim
+
+    	rather than:
+    	@verbatim
+    	if ( ele->Attribute( "foo" ) ) {
+    		if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
+    	}
+    	@endverbatim
+    */
+    const char* Attribute( const char* name, const char* value=0 ) const;
+
+    /** Given an attribute name, IntAttribute() returns the value
+    	of the attribute interpreted as an integer. The default
+        value will be returned if the attribute isn't present,
+        or if there is an error. (For a method with error
+    	checking, see QueryIntAttribute()).
+    */
+	int IntAttribute(const char* name, int defaultValue = 0) const;
+    /// See IntAttribute()
+	unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
+	/// See IntAttribute()
+	int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
+	/// See IntAttribute()
+	bool BoolAttribute(const char* name, bool defaultValue = false) const;
+    /// See IntAttribute()
+	double DoubleAttribute(const char* name, double defaultValue = 0) const;
+    /// See IntAttribute()
+	float FloatAttribute(const char* name, float defaultValue = 0) const;
+
+    /** Given an attribute name, QueryIntAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryIntAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+    XMLError QueryIntAttribute( const char* name, int* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryIntValue( value );
+    }
+
+	/// See QueryIntAttribute()
+    XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const	{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryUnsignedValue( value );
+    }
+
+	/// See QueryIntAttribute()
+	XMLError QueryInt64Attribute(const char* name, int64_t* value) const {
+		const XMLAttribute* a = FindAttribute(name);
+		if (!a) {
+			return XML_NO_ATTRIBUTE;
+		}
+		return a->QueryInt64Value(value);
+	}
+
+	/// See QueryIntAttribute()
+    XMLError QueryBoolAttribute( const char* name, bool* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryBoolValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryDoubleAttribute( const char* name, double* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryDoubleValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryFloatAttribute( const char* name, float* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryFloatValue( value );
+    }
+
+	/// See QueryIntAttribute()
+	XMLError QueryStringAttribute(const char* name, const char** value) const {
+		const XMLAttribute* a = FindAttribute(name);
+		if (!a) {
+			return XML_NO_ATTRIBUTE;
+		}
+		*value = a->Value();
+		return XML_SUCCESS;
+	}
+
+
+
+    /** Given an attribute name, QueryAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. It is overloaded for the primitive types,
+		and is a generally more convenient replacement of
+		QueryIntAttribute() and related functions.
+
+		If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+	XMLError QueryAttribute( const char* name, int* value ) const {
+		return QueryIntAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, unsigned int* value ) const {
+		return QueryUnsignedAttribute( name, value );
+	}
+
+	XMLError QueryAttribute(const char* name, int64_t* value) const {
+		return QueryInt64Attribute(name, value);
+	}
+
+	XMLError QueryAttribute( const char* name, bool* value ) const {
+		return QueryBoolAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, double* value ) const {
+		return QueryDoubleAttribute( name, value );
+	}
+
+	XMLError QueryAttribute( const char* name, float* value ) const {
+		return QueryFloatAttribute( name, value );
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, const char* value )	{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, int value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, unsigned value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+	/// Sets the named attribute to value.
+	void SetAttribute(const char* name, int64_t value) {
+		XMLAttribute* a = FindOrCreateAttribute(name);
+		a->SetAttribute(value);
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, bool value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, double value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, float value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+    /**
+    	Delete an attribute.
+    */
+    void DeleteAttribute( const char* name );
+
+    /// Return the first attribute in the list.
+    const XMLAttribute* FirstAttribute() const {
+        return _rootAttribute;
+    }
+    /// Query a specific attribute in the list.
+    const XMLAttribute* FindAttribute( const char* name ) const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, GetText() is limited compared to getting the XMLText child
+    	and accessing it directly.
+
+    	If the first child of 'this' is a XMLText, the GetText()
+    	returns the character string of the Text node, else null is returned.
+
+    	This is a convenient method for getting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		const char* str = fooElement->GetText();
+    	@endverbatim
+
+    	'str' will be a pointer to "This is text".
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then the value of str would be null. The first child node isn't a text node, it is
+    	another element. From this XML:
+    	@verbatim
+    		<foo>This is <b>text</b></foo>
+    	@endverbatim
+    	GetText() will return "This is ".
+    */
+    const char* GetText() const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, SetText() is limited compared to creating an XMLText child
+    	and mutating it directly.
+
+    	If the first child of 'this' is a XMLText, SetText() sets its value to
+		the given string, otherwise it will create a first child that is an XMLText.
+
+    	This is a convenient method for setting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		fooElement->SetText( "Hullaballoo!" );
+     	<foo>Hullaballoo!</foo>
+		@endverbatim
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then it will not change "This is text", but rather prefix it with a text element:
+    	@verbatim
+    		<foo>Hullaballoo!<b>This is text</b></foo>
+    	@endverbatim
+
+		For this XML:
+    	@verbatim
+    		<foo />
+    	@endverbatim
+    	SetText() will generate
+    	@verbatim
+    		<foo>Hullaballoo!</foo>
+    	@endverbatim
+    */
+	void SetText( const char* inText );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( int value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( unsigned value );
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+	void SetText(int64_t value);
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( bool value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( double value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( float value );
+
+    /**
+    	Convenience method to query the value of a child text node. This is probably best
+    	shown by example. Given you have a document is this form:
+    	@verbatim
+    		<point>
+    			<x>1</x>
+    			<y>1.4</y>
+    		</point>
+    	@endverbatim
+
+    	The QueryIntText() and similar functions provide a safe and easier way to get to the
+    	"value" of x and y.
+
+    	@verbatim
+    		int x = 0;
+    		float y = 0;	// types of x and y are contrived for example
+    		const XMLElement* xElement = pointElement->FirstChildElement( "x" );
+    		const XMLElement* yElement = pointElement->FirstChildElement( "y" );
+    		xElement->QueryIntText( &x );
+    		yElement->QueryFloatText( &y );
+    	@endverbatim
+
+    	@returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
+    			 to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
+
+    */
+    XMLError QueryIntText( int* ival ) const;
+    /// See QueryIntText()
+    XMLError QueryUnsignedText( unsigned* uval ) const;
+	/// See QueryIntText()
+	XMLError QueryInt64Text(int64_t* uval) const;
+	/// See QueryIntText()
+    XMLError QueryBoolText( bool* bval ) const;
+    /// See QueryIntText()
+    XMLError QueryDoubleText( double* dval ) const;
+    /// See QueryIntText()
+    XMLError QueryFloatText( float* fval ) const;
+
+	int IntText(int defaultValue = 0) const;
+
+	/// See QueryIntText()
+	unsigned UnsignedText(unsigned defaultValue = 0) const;
+	/// See QueryIntText()
+	int64_t Int64Text(int64_t defaultValue = 0) const;
+	/// See QueryIntText()
+	bool BoolText(bool defaultValue = false) const;
+	/// See QueryIntText()
+	double DoubleText(double defaultValue = 0) const;
+	/// See QueryIntText()
+	float FloatText(float defaultValue = 0) const;
+
+    // internal:
+    enum ElementClosingType {
+        OPEN,		// <foo>
+        CLOSED,		// <foo/>
+        CLOSING		// </foo>
+    };
+    ElementClosingType ClosingType() const {
+        return _closingType;
+    }
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLElement( XMLDocument* doc );
+    virtual ~XMLElement();
+    XMLElement( const XMLElement& );	// not supported
+    void operator=( const XMLElement& );	// not supported
+
+    XMLAttribute* FindOrCreateAttribute( const char* name );
+    char* ParseAttributes( char* p, int* curLineNumPtr );
+    static void DeleteAttribute( XMLAttribute* attribute );
+    XMLAttribute* CreateAttribute();
+
+    enum { BUF_SIZE = 200 };
+    ElementClosingType _closingType;
+    // The attribute list is ordered; there is no 'lastAttribute'
+    // because the list needs to be scanned for dupes before adding
+    // a new attribute.
+    XMLAttribute* _rootAttribute;
+};
+
+
+enum Whitespace {
+    PRESERVE_WHITESPACE,
+    COLLAPSE_WHITESPACE
+};
+
+
+/** A Document binds together all the functionality.
+	It can be saved, loaded, and printed to the screen.
+	All Nodes are connected and allocated to a Document.
+	If the Document is deleted, all its Nodes are also deleted.
+*/
+class TINYXML2_LIB XMLDocument : public XMLNode
+{
+    friend class XMLElement;
+    // Gives access to SetError and Push/PopDepth, but over-access for everything else.
+    // Wishing C++ had "internal" scope.
+    friend class XMLNode;
+    friend class XMLText;
+    friend class XMLComment;
+    friend class XMLDeclaration;
+    friend class XMLUnknown;
+public:
+    /// constructor
+    XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
+    ~XMLDocument();
+
+    virtual XMLDocument* ToDocument()				{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+    virtual const XMLDocument* ToDocument() const	{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+
+    /**
+    	Parse an XML file from a character string.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+
+    	You may optionally pass in the 'nBytes', which is
+    	the number of bytes which will be parsed. If not
+    	specified, TinyXML-2 will assume 'xml' points to a
+    	null terminated string.
+    */
+    XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
+
+    /**
+    	Load an XML file from disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( const char* filename );
+
+    /**
+    	Load an XML file from disk. You are responsible
+    	for providing and closing the FILE*.
+
+        NOTE: The file should be opened as binary ("rb")
+        not text in order for TinyXML-2 to correctly
+        do newline normalization.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( FILE* );
+
+    /**
+    	Save the XML file to disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( const char* filename, bool compact = false );
+
+    /**
+    	Save the XML file to disk. You are responsible
+    	for providing and closing the FILE*.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( FILE* fp, bool compact = false );
+
+    bool ProcessEntities() const		{
+        return _processEntities;
+    }
+    Whitespace WhitespaceMode() const	{
+        return _whitespaceMode;
+    }
+
+    /**
+    	Returns true if this document has a leading Byte Order Mark of UTF8.
+    */
+    bool HasBOM() const {
+        return _writeBOM;
+    }
+    /** Sets whether to write the BOM when writing the file.
+    */
+    void SetBOM( bool useBOM ) {
+        _writeBOM = useBOM;
+    }
+
+    /** Return the root element of DOM. Equivalent to FirstChildElement().
+        To get the first node, use FirstChild().
+    */
+    XMLElement* RootElement()				{
+        return FirstChildElement();
+    }
+    const XMLElement* RootElement() const	{
+        return FirstChildElement();
+    }
+
+    /** Print the Document. If the Printer is not provided, it will
+        print to stdout. If you provide Printer, this can print to a file:
+    	@verbatim
+    	XMLPrinter printer( fp );
+    	doc.Print( &printer );
+    	@endverbatim
+
+    	Or you can use a printer to print to memory:
+    	@verbatim
+    	XMLPrinter printer;
+    	doc.Print( &printer );
+    	// printer.CStr() has a const char* to the XML
+    	@endverbatim
+    */
+    void Print( XMLPrinter* streamer=0 ) const;
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /**
+    	Create a new Element associated with
+    	this Document. The memory for the Element
+    	is managed by the Document.
+    */
+    XMLElement* NewElement( const char* name );
+    /**
+    	Create a new Comment associated with
+    	this Document. The memory for the Comment
+    	is managed by the Document.
+    */
+    XMLComment* NewComment( const char* comment );
+    /**
+    	Create a new Text associated with
+    	this Document. The memory for the Text
+    	is managed by the Document.
+    */
+    XMLText* NewText( const char* text );
+    /**
+    	Create a new Declaration associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+
+    	If the 'text' param is null, the standard
+    	declaration is used.:
+    	@verbatim
+    		<?xml version="1.0" encoding="UTF-8"?>
+    	@endverbatim
+    */
+    XMLDeclaration* NewDeclaration( const char* text=0 );
+    /**
+    	Create a new Unknown associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+    */
+    XMLUnknown* NewUnknown( const char* text );
+
+    /**
+    	Delete a node associated with this document.
+    	It will be unlinked from the DOM.
+    */
+    void DeleteNode( XMLNode* node );
+
+    void ClearError() {
+        SetError(XML_SUCCESS, 0, 0);
+    }
+
+    /// Return true if there was an error parsing the document.
+    bool Error() const {
+        return _errorID != XML_SUCCESS;
+    }
+    /// Return the errorID.
+    XMLError  ErrorID() const {
+        return _errorID;
+    }
+	const char* ErrorName() const;
+    static const char* ErrorIDToName(XMLError errorID);
+
+    /** Returns a "long form" error description. A hopefully helpful
+        diagnostic with location, line number, and/or additional info.
+    */
+	const char* ErrorStr() const;
+
+    /// A (trivial) utility function that prints the ErrorStr() to stdout.
+    void PrintError() const;
+
+    /// Return the line where the error occurred, or zero if unknown.
+    int ErrorLineNum() const
+    {
+        return _errorLineNum;
+    }
+
+    /// Clear the document, resetting it to the initial state.
+    void Clear();
+
+	/**
+		Copies this document to a target document.
+		The target will be completely cleared before the copy.
+		If you want to copy a sub-tree, see XMLNode::DeepClone().
+
+		NOTE: that the 'target' must be non-null.
+	*/
+	void DeepCopy(XMLDocument* target) const;
+
+	// internal
+    char* Identify( char* p, XMLNode** node );
+
+	// internal
+	void MarkInUse(XMLNode*);
+
+    virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const	{
+        return 0;
+    }
+    virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const	{
+        return false;
+    }
+
+private:
+    XMLDocument( const XMLDocument& );	// not supported
+    void operator=( const XMLDocument& );	// not supported
+
+    bool			_writeBOM;
+    bool			_processEntities;
+    XMLError		_errorID;
+    Whitespace		_whitespaceMode;
+    mutable StrPair	_errorStr;
+    int             _errorLineNum;
+    char*			_charBuffer;
+    int				_parseCurLineNum;
+	int				_parsingDepth;
+	// Memory tracking does add some overhead.
+	// However, the code assumes that you don't
+	// have a bunch of unlinked nodes around.
+	// Therefore it takes less memory to track
+	// in the document vs. a linked list in the XMLNode,
+	// and the performance is the same.
+	DynArray<XMLNode*, 10> _unlinked;
+
+    MemPoolT< sizeof(XMLElement) >	 _elementPool;
+    MemPoolT< sizeof(XMLAttribute) > _attributePool;
+    MemPoolT< sizeof(XMLText) >		 _textPool;
+    MemPoolT< sizeof(XMLComment) >	 _commentPool;
+
+	static const char* _errorNames[XML_ERROR_COUNT];
+
+    void Parse();
+
+    void SetError( XMLError error, int lineNum, const char* format, ... );
+
+	// Something of an obvious security hole, once it was discovered.
+	// Either an ill-formed XML or an excessively deep one can overflow
+	// the stack. Track stack depth, and error out if needed.
+	class DepthTracker {
+	public:
+		explicit DepthTracker(XMLDocument * document) {
+			this->_document = document;
+			document->PushDepth();
+		}
+		~DepthTracker() {
+			_document->PopDepth();
+		}
+	private:
+		XMLDocument * _document;
+	};
+	void PushDepth();
+	void PopDepth();
+
+    template<class NodeType, int PoolElementSize>
+    NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
+};
+
+template<class NodeType, int PoolElementSize>
+inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
+{
+    TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );
+    TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );
+    NodeType* returnNode = new (pool.Alloc()) NodeType( this );
+    TIXMLASSERT( returnNode );
+    returnNode->_memPool = &pool;
+
+	_unlinked.Push(returnNode);
+    return returnNode;
+}
+
+/**
+	A XMLHandle is a class that wraps a node pointer with null checks; this is
+	an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2
+	DOM structure. It is a separate utility class.
+
+	Take an example:
+	@verbatim
+	<Document>
+		<Element attributeA = "valueA">
+			<Child attributeB = "value1" />
+			<Child attributeB = "value2" />
+		</Element>
+	</Document>
+	@endverbatim
+
+	Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+	easy to write a *lot* of code that looks like:
+
+	@verbatim
+	XMLElement* root = document.FirstChildElement( "Document" );
+	if ( root )
+	{
+		XMLElement* element = root->FirstChildElement( "Element" );
+		if ( element )
+		{
+			XMLElement* child = element->FirstChildElement( "Child" );
+			if ( child )
+			{
+				XMLElement* child2 = child->NextSiblingElement( "Child" );
+				if ( child2 )
+				{
+					// Finally do something useful.
+	@endverbatim
+
+	And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
+	of such code. A XMLHandle checks for null pointers so it is perfectly safe
+	and correct to use:
+
+	@verbatim
+	XMLHandle docHandle( &document );
+	XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement();
+	if ( child2 )
+	{
+		// do something useful
+	@endverbatim
+
+	Which is MUCH more concise and useful.
+
+	It is also safe to copy handles - internally they are nothing more than node pointers.
+	@verbatim
+	XMLHandle handleCopy = handle;
+	@endverbatim
+
+	See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
+*/
+class TINYXML2_LIB XMLHandle
+{
+public:
+    /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+    explicit XMLHandle( XMLNode* node ) : _node( node ) {
+    }
+    /// Create a handle from a node.
+    explicit XMLHandle( XMLNode& node ) : _node( &node ) {
+    }
+    /// Copy constructor
+    XMLHandle( const XMLHandle& ref ) : _node( ref._node ) {
+    }
+    /// Assignment
+    XMLHandle& operator=( const XMLHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    /// Get the first child of this handle.
+    XMLHandle FirstChild() 													{
+        return XMLHandle( _node ? _node->FirstChild() : 0 );
+    }
+    /// Get the first child element of this handle.
+    XMLHandle FirstChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    /// Get the last child of this handle.
+    XMLHandle LastChild()													{
+        return XMLHandle( _node ? _node->LastChild() : 0 );
+    }
+    /// Get the last child element of this handle.
+    XMLHandle LastChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    /// Get the previous sibling of this handle.
+    XMLHandle PreviousSibling()												{
+        return XMLHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    /// Get the previous sibling element of this handle.
+    XMLHandle PreviousSiblingElement( const char* name = 0 )				{
+        return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    /// Get the next sibling of this handle.
+    XMLHandle NextSibling()													{
+        return XMLHandle( _node ? _node->NextSibling() : 0 );
+    }
+    /// Get the next sibling element of this handle.
+    XMLHandle NextSiblingElement( const char* name = 0 )					{
+        return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+    /// Safe cast to XMLNode. This can return null.
+    XMLNode* ToNode()							{
+        return _node;
+    }
+    /// Safe cast to XMLElement. This can return null.
+    XMLElement* ToElement() 					{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    /// Safe cast to XMLText. This can return null.
+    XMLText* ToText() 							{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    /// Safe cast to XMLUnknown. This can return null.
+    XMLUnknown* ToUnknown() 					{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    /// Safe cast to XMLDeclaration. This can return null.
+    XMLDeclaration* ToDeclaration() 			{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    XMLNode* _node;
+};
+
+
+/**
+	A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
+	same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
+*/
+class TINYXML2_LIB XMLConstHandle
+{
+public:
+    explicit XMLConstHandle( const XMLNode* node ) : _node( node ) {
+    }
+    explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) {
+    }
+    XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) {
+    }
+
+    XMLConstHandle& operator=( const XMLConstHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    const XMLConstHandle FirstChild() const											{
+        return XMLConstHandle( _node ? _node->FirstChild() : 0 );
+    }
+    const XMLConstHandle FirstChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    const XMLConstHandle LastChild()	const										{
+        return XMLConstHandle( _node ? _node->LastChild() : 0 );
+    }
+    const XMLConstHandle LastChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    const XMLConstHandle PreviousSibling() const									{
+        return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const		{
+        return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    const XMLConstHandle NextSibling() const										{
+        return XMLConstHandle( _node ? _node->NextSibling() : 0 );
+    }
+    const XMLConstHandle NextSiblingElement( const char* name = 0 ) const			{
+        return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+
+    const XMLNode* ToNode() const				{
+        return _node;
+    }
+    const XMLElement* ToElement() const			{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    const XMLText* ToText() const				{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    const XMLUnknown* ToUnknown() const			{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    const XMLDeclaration* ToDeclaration() const	{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    const XMLNode* _node;
+};
+
+
+/**
+	Printing functionality. The XMLPrinter gives you more
+	options than the XMLDocument::Print() method.
+
+	It can:
+	-# Print to memory.
+	-# Print to a file you provide.
+	-# Print XML without a XMLDocument.
+
+	Print to Memory
+
+	@verbatim
+	XMLPrinter printer;
+	doc.Print( &printer );
+	SomeFunction( printer.CStr() );
+	@endverbatim
+
+	Print to a File
+
+	You provide the file pointer.
+	@verbatim
+	XMLPrinter printer( fp );
+	doc.Print( &printer );
+	@endverbatim
+
+	Print without a XMLDocument
+
+	When loading, an XML parser is very useful. However, sometimes
+	when saving, it just gets in the way. The code is often set up
+	for streaming, and constructing the DOM is just overhead.
+
+	The Printer supports the streaming case. The following code
+	prints out a trivially simple XML file without ever creating
+	an XML document.
+
+	@verbatim
+	XMLPrinter printer( fp );
+	printer.OpenElement( "foo" );
+	printer.PushAttribute( "foo", "bar" );
+	printer.CloseElement();
+	@endverbatim
+*/
+class TINYXML2_LIB XMLPrinter : public XMLVisitor
+{
+public:
+    /** Construct the printer. If the FILE* is specified,
+    	this will print to the FILE. Else it will print
+    	to memory, and the result is available in CStr().
+    	If 'compact' is set to true, then output is created
+    	with only required whitespace and newlines.
+    */
+    XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 );
+    virtual ~XMLPrinter()	{}
+
+    /** If streaming, write the BOM and declaration. */
+    void PushHeader( bool writeBOM, bool writeDeclaration );
+    /** If streaming, start writing an element.
+        The element must be closed with CloseElement()
+    */
+    void OpenElement( const char* name, bool compactMode=false );
+    /// If streaming, add an attribute to an open element.
+    void PushAttribute( const char* name, const char* value );
+    void PushAttribute( const char* name, int value );
+    void PushAttribute( const char* name, unsigned value );
+	void PushAttribute(const char* name, int64_t value);
+	void PushAttribute( const char* name, bool value );
+    void PushAttribute( const char* name, double value );
+    /// If streaming, close the Element.
+    virtual void CloseElement( bool compactMode=false );
+
+    /// Add a text node.
+    void PushText( const char* text, bool cdata=false );
+    /// Add a text node from an integer.
+    void PushText( int value );
+    /// Add a text node from an unsigned.
+    void PushText( unsigned value );
+	/// Add a text node from an unsigned.
+	void PushText(int64_t value);
+	/// Add a text node from a bool.
+    void PushText( bool value );
+    /// Add a text node from a float.
+    void PushText( float value );
+    /// Add a text node from a double.
+    void PushText( double value );
+
+    /// Add a comment
+    void PushComment( const char* comment );
+
+    void PushDeclaration( const char* value );
+    void PushUnknown( const char* value );
+
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ );
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
+    virtual bool VisitExit( const XMLElement& element );
+
+    virtual bool Visit( const XMLText& text );
+    virtual bool Visit( const XMLComment& comment );
+    virtual bool Visit( const XMLDeclaration& declaration );
+    virtual bool Visit( const XMLUnknown& unknown );
+
+    /**
+    	If in print to memory mode, return a pointer to
+    	the XML file in memory.
+    */
+    const char* CStr() const {
+        return _buffer.Mem();
+    }
+    /**
+    	If in print to memory mode, return the size
+    	of the XML file in memory. (Note the size returned
+    	includes the terminating null.)
+    */
+    int CStrSize() const {
+        return _buffer.Size();
+    }
+    /**
+    	If in print to memory mode, reset the buffer to the
+    	beginning.
+    */
+    void ClearBuffer() {
+        _buffer.Clear();
+        _buffer.Push(0);
+		_firstElement = true;
+    }
+
+protected:
+	virtual bool CompactMode( const XMLElement& )	{ return _compactMode; }
+
+	/** Prints out the space before an element. You may override to change
+	    the space and tabs used. A PrintSpace() override should call Print().
+	*/
+    virtual void PrintSpace( int depth );
+    void Print( const char* format, ... );
+    void Write( const char* data, size_t size );
+    inline void Write( const char* data )           { Write( data, strlen( data ) ); }
+    void Putc( char ch );
+
+    void SealElementIfJustOpened();
+    bool _elementJustOpened;
+    DynArray< const char*, 10 > _stack;
+
+private:
+    void PrintString( const char*, bool restrictedEntitySet );	// prints out, after detecting entities.
+
+    bool _firstElement;
+    FILE* _fp;
+    int _depth;
+    int _textDepth;
+    bool _processEntities;
+	bool _compactMode;
+
+    enum {
+        ENTITY_RANGE = 64,
+        BUF_SIZE = 200
+    };
+    bool _entityFlag[ENTITY_RANGE];
+    bool _restrictedEntityFlag[ENTITY_RANGE];
+
+    DynArray< char, 20 > _buffer;
+
+    // Prohibit cloning, intentionally not implemented
+    XMLPrinter( const XMLPrinter& );
+    XMLPrinter& operator=( const XMLPrinter& );
+};
+
+
+}	// tinyxml2
+
+#if defined(_MSC_VER)
+#   pragma warning(pop)
+#endif
+
+#endif // TINYXML2_INCLUDED

+ 51 - 0
ScbcCopyKey/test.py

@@ -0,0 +1,51 @@
+# -*- coding: UTF-8 -*-
+import io,sys,time
+import ScbcCopyKey
+import binascii
+
+if __name__ == "__main__":
+    # 打开串口;
+    result = ScbcCopyKey.Open(5, 115200, 8, 0, 1)
+    if result:
+        # 进入工厂模式;
+        if ScbcCopyKey.EnterFactory():
+            print u"获取ProjectId",ScbcCopyKey.GetProjectId()           
+            print u"获取软件版本",ScbcCopyKey.GetSoftVersion()
+            print u"获取DeviceID",ScbcCopyKey.GetDeviceId()
+            print u"获取ClientType",ScbcCopyKey.GetClientType()
+            print u"获取MAC",ScbcCopyKey.GetMAC()
+            print u"获取HDCP",ScbcCopyKey.GetHDCPKey()
+            print u"获取HDCP22",ScbcCopyKey.GetHDCPKey22()
+            print u"GetWidi",ScbcCopyKey.GetWidi()
+            print u"GetNetflixESN",ScbcCopyKey.GetNetflixESN()
+            print u"GetWidevine",ScbcCopyKey.GetWidevine()
+            print u"GetCiKey",ScbcCopyKey.GetCiKey()
+            print u"GetOSDLanguage",ScbcCopyKey.GetOSDLanguage()
+            print u"GetShopLanguage",ScbcCopyKey.GetShopLanguage()
+            print u"GetChannel",ScbcCopyKey.GetChannel()
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\DID\did1711.bin"
+            print u"SetDeviceId", ScbcCopyKey.SetDeviceIdBF(file)
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\MAC\TCL_MAC_CC-A1-2B-09-01-C3.bin"
+            print u"SetMAC", ScbcCopyKey.SetMACBF(file)
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\RealTek1.4\Encoder3_00000004.bin"
+            print u"SetHDCPBF", ScbcCopyKey.SetHDCPKeyBF(file)
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\RealTek2.2\PUBHDCP22_0000_20190710_0053303.bin"
+            print u"SetHDCP22BF", ScbcCopyKey.SetHDCPKey22BF(file)
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\RT2851 CI KEY\00000478-0735185308117697.bin"
+            print u"SetCiKeyBF", ScbcCopyKey.SetCiKeyBF(file)
+
+            file = r"D:\SAT\Allkey\RT2851-1PCS\RT2851 widevine\RT2851-Widevine_111"
+            print u"SetWidevineBF", ScbcCopyKey.SetWidevineBF(file)
+
+            # 离开工厂模式;
+            ScbcCopyKey.LeaveFactory()
+        #endif
+
+        # 关闭串口
+        ScbcCopyKey.Close()
+    #endif