/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbODconfig
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"
#include "../../xbConfig.h"
#include "xbODregistry.h"
#include <shlobj.h>
#include "../../xbFileTools.h"
#include "../../xbLibTools.h"

#ifdef xbOS_MSWIN

#pragma warning(push)
#pragma warning(disable: 4311) //'cast de type' : troncation de pointeur de 'const void *' à 'ulong'
#pragma warning(disable: 4312) //'cast de type' : la conversion de 'LCID' en 'LPCVOID' d'une taille supérieure
extern xbXdcLib g_xdcBaseDLL;

xbString xbCfgEngine::GetCharsetFromCP(ulong cp,xbOpMode opm) const
{
	switch(cp)
	{
	case CP_ACP:
		cp = ::GetACP();
		break;
	case CP_OEMCP:
		cp = ::GetOEMCP();
		break;
	}

	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Codepage\\"+xbString(cp));

	if (!key.Exists() || !key.ValueExists("BodyCharset"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST, "Code page "+xbString(cp)+" unknown", "xbCfgEngine::GetCharsetFromCP()");
		return xbString();
	}

	return key.Value["BodyCharset"].AsString;
}

ulong xbCfgEngine::GetCPfromCharset(const xbString &_charset,xbOpMode opm) const
{
	xbString charset(_charset);
	if (charset.IsEmpty() || charset.CompareNoCase("localcp") == 0)
		return ::GetACP();
	if (charset.CompareNoCase("oemcp") == 0)
		return ::GetOEMCP();
	if (charset.CompareNoCase("utf-8") == 0)
		return CP_UTF8;
	int cnt = 0;
	for(;;)
	{
		if (charset.IsEmpty())
		{
			if (opm == xbOp_Required)
			{
				if (cnt > 0)
					xbThrow xbEappCfg("Configuration is invalid for charset "+_charset+", alias is empty", "xbCfgEngine::GetCPfromCharset(), rec cnt="+xbString(cnt));
				xbThrow xbEapp("Charset argument is empty", "xbCfgEngine::GetCPfromCharset()");
			}
			return 0;
		}
		xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Charset\\"+charset);
		if (!key.Exists())
		{
			if (charset.Length>2 && xbStrnicmp(charset,"cp",2)==0)
			{
				ulong cp = xbStrToUlong(charset.Mid(2));
				if (cp && ::IsValidCodePage(cp))
					return cp;
			}
			if (opm == xbOp_Required)
			{
				if (cnt > 0)
					xbThrow xbEappCfg("Configuration is invalid for charset "+_charset+", alias("+charset+") doesn't exist", "xbCfgEngine::GetCPfromCharset(), rec cnt="+xbString(cnt));
				xbThrow xbEapp(xbHR_LAST, "Charset "+charset+" unknown", "xbCfgEngine::GetCPfromCharset()");
			}
			return 0;
		}
		if (key.ValueExists("InternetEncoding"))
		{
			ulong cp = key.Value["InternetEncoding"].AsUlong;
			if (opm == xbOp_Required && !cp)
				xbThrow xbEappCfg("Configuration is invalid for charset "+_charset+"  (alias: "+charset+"), codepage is invalid for this entry", "xbCfgEngine::GetCPfromCharset(), rec cnt="+xbString(cnt));
			return cp;
		}
		if (key.ValueExists("AliasForCharset"))
		{
			if (cnt > 2)
			{
				if (opm == xbOp_Required)
					xbThrow xbEappCfg("Configuration is invalid for charset "+_charset+", recursion count limit exceeded", "xbCfgEngine::GetCPfromCharset(), alias="+charset+", rec cnt="+xbString(cnt));
				return 0;
			}
			cnt++;
			charset = key.Value["AliasForCharset"].AsString;
			continue;
		}
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Configuration is invalid for charset "+_charset+"  (alias: "+charset+"), there is no codepage and no alias defined for this entry", "xbCfgEngine::GetCPfromCharset(), rec cnt="+xbString(cnt));
		return 0;
	}
}

ulong xbCfgEngine::GetRealCP(ulong cp,xbOpMode opm) const
{
	switch(cp)
	{
	case CP_ACP:
		return ::GetACP();
	case CP_OEMCP:
		return ::GetOEMCP();
	}
	if (!::IsValidCodePage(cp))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Invalid or not supported code page ("+xbString(cp)+")","xbCfgEngine::GetRealCP()");
		return 0;
	}
	return cp;
}

bool xbCfgEngine::IsValidCP(ulong cp,bool bInstalled) const
{
	return ::IsValidCodePage(cp)?true:false;
}

bool xbCfgEngine::IsValidCharset(const xbString &charset,bool bInstalled) const
{
	ulong cp = GetCPfromCharset(charset,xbOp_Optional);
	if (!cp)
		return false;
	return ::IsValidCodePage(cp)?true:false;
}

bool xbCfgEngine::EnumCP(int &idx, ulong *cp, xbString *charset,bool bInstalled)
{
	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Codepage");
	
	xbODregEnumKey enumKey(key); 
	xbODregKey cpKey;
	enumKey.setPosition(idx);

	for(;;)
	{
		if (!enumKey.Next(&cpKey))
			return false;

		idx = enumKey.getPosition();

		ulong ncp;
		if (::xbStrToUlong(cpKey.Name,&ncp,10,NULL)<=0)
			continue;

		if (!cpKey.ValueExists("BodyCharset"))
			continue;

		if (!IsValidCP(ncp))
			continue;

		if (cp)
			*cp = ncp;
		if (charset)
			*charset = key.Value["BodyCharset"].AsString;
		return true;
	}
}

xbString xbCfgEngine::GetContentTypeFromPath(const xbString &path,xbOpMode opm) const
{
	xbString ext(::xbGetFileExt(path,true));
	if (ext.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp("Invalid path ("+path+") to find content type","xbCfgEngine::GetContentTypeFromPath()");
		return xbString();
	}

	xbODregKey key(xbODregClassesRoot,ext);
	if (!key.Exists())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file '"+path+"'","xbCfgEngine::GetContentTypeFromPath(), extension not registered");
		return xbString();
	}
	if (!key.ValueExists("Content Type"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file '"+path+"'","xbCfgEngine::GetContentTypeFromPath(), no content type defined");
		return xbString();
	}
	xbString ct(key.Value["Content Type"].AsString);
	ct.Trim();
	if (ct.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file '"+path+"'","xbCfgEngine::GetContentTypeFromPath(), content type is empty");
		return xbString();
	}
	return ct;
}

xbString xbCfgEngine::GetContentTypeFromExt(const xbString &ext,xbOpMode opm) const
{
	if (ext.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp("Invalid extension to find content type","xbCfgEngine::GetContentTypeFromPath(), extension is empty");
		return xbString();
	}

	xbODregKey key(xbODregClassesRoot,ext);
	if (!key.Exists())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file extension '"+ext+"'","xbCfgEngine::GetContentTypeFromPath(), extension not registered");
		return xbString();
	}
	if (!key.ValueExists("Content Type"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file extension '"+ext+"'","xbCfgEngine::GetContentTypeFromPath(), no content type defined");
		return xbString();
	}
	xbString ct(key.Value["Content Type"].AsString);
	ct.Trim();
	if (ct.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown content type for file extension '"+ext+"'","xbCfgEngine::GetContentTypeFromPath(), content type is empty");
		return xbString();
	}
	return ct;
}

xbString xbCfgEngine::GetExtFromContentType(const xbString &ct,xbOpMode opm) const
{
//HKEY_CLASSES_ROOT\MIME\Database\Content Type
	if (ct.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp("Invalid content type to find extension","xbCfgEngine::GetExtFromContentType(), content type is empty");
		return xbString();
	}

	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Content Type\\"+ct);
	if (!key.Exists())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown extension for file content type '"+ct+"'","xbCfgEngine::GetExtFromContentType(), content type not registered");
		return xbString();
	}
	if (!key.ValueExists("Extension"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown extension for file content type '"+ct+"'","xbCfgEngine::GetExtFromContentType(), no extension defined");
		return xbString();
	}
	xbString ext(key.Value["Extension"].AsString);
	ext.Trim();
	if (ext.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown extension for file content type '"+ct+"'","xbCfgEngine::GetExtFromContentType(), extension is empty");
		return xbString();
	}
	return ext;
//	xbThrow xbEapp(E_NOTIMPL,"Can't retrieve extension from content type, function not implemented","xbCfgEngine::GetExtFromContentType()");
}

bool xbCfgEngine::IsValidContentType(const xbString &ct) const
{
	if (ct.IsEmpty())
		return false;
	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Content Type\\"+ct);
	return key.Exists();
}

bool xbCfgEngine::IsValidExtension(const xbString &ext) const
{
	if (ext.IsEmpty())
		return false;

	xbODregKey key(xbODregClassesRoot,ext);
	return key.Exists();
}

bool xbCfgEngine::EnumContentType(int &idx, xbString *ext, xbString *ct)
{
	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Content Type");
	
	xbODregEnumKey enumKey(key); 
	xbODregKey ctKey;
	enumKey.setPosition(idx);

	for(;;)
	{
		if (!enumKey.Next(&ctKey))
			return false;

		idx = enumKey.getPosition();

		if (ct)
			*ct = ctKey.Name;

		if (ext)
			*ext = key.Value["Extension"].AsString;
		return true;
	}
}

int		xbCfgEngine::lcidFindNameCB(LCIDitem* const &pItem1,const void *_pItem2)
{
	return xbStricmp(pItem1->m_name,(LPCSTR)_pItem2);
}

int		xbCfgEngine::lcidFindCodeCB(LCIDitem* const &pItem1,const void *_pItem2)
{
	if (pItem1->m_lcid > (ulong)_pItem2)
		return 1;
	if (pItem1->m_lcid < (ulong)_pItem2)
		return -1;
	return 0;
}

void xbCfgEngine::LCIDload()
{
	xbSectLock lck(&m_sectCache);

	m_arrLcid.Empty();
	m_arrLcidByCode.Empty();

	xbODregKey key(xbODregClassesRoot,"MIME\\Database\\Rfc1766");
	if (!key.Exists())
		return;
	xbODregEnumValue valEnum(key);
	xbString vname;
	xbVariant vval;
	while(valEnum.Next(&vname,&vval))
	{
		ulong lcid;
		if (::xbStrToUlong(vname,&lcid,16,NULL)<=0)
			continue;
		xbString name,label;
		vval.AsString.GetToken(0,";",&name,&label);

		int pos,pos2;
		if (m_arrLcid.SortedFind((LPCSTR)name,lcidFindNameCB,&pos) != NULL)
			continue;
		if (m_arrLcidByCode.SortedFind((LPCVOID)lcid,lcidFindCodeCB,&pos2) != NULL)
			continue;

		LCIDitem *pItem = new LCIDitem(lcid,name,label);
		m_arrLcid.Insert(pos,pItem);
		m_arrLcidByCode.Insert(pos2,pItem);
	}
}

xbString xbCfgEngine::GetNameFromLCID(LCID lcid,xbOpMode opm) const
{
	if (!(lcid = GetRealLCID(lcid,opm)))
		return xbString();

	xbSectLock lck(&m_sectCache);
	if (m_arrLcid.IsEmpty())
		((xbCfgEngine*)this)->LCIDload();

	LCIDitem *const *ppItem = m_arrLcidByCode.SortedFind((LPCVOID)(lcid&0xFFFF),lcidFindCodeCB);
	if (!ppItem)
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown locale id '"+xbString(lcid,xbStringNum_hexa)+"'","xbCfgEngine::GetNameFromLCID(), locale id not found");
		return xbString();
	}

	return (*ppItem)->m_name;
}

xbString xbCfgEngine::GetLabelFromLCID(LCID lcid,xbOpMode opm) const
{
	if (!(lcid = GetRealLCID(lcid,opm)))
		return xbString();

	xbSectLock lck(&m_sectCache);
	if (m_arrLcid.IsEmpty())
		((xbCfgEngine*)this)->LCIDload();

	LCIDitem *const *ppItem = m_arrLcidByCode.SortedFind((LPCVOID)(lcid&0xFFFF),lcidFindCodeCB);
	if (!ppItem)
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown locale id '"+xbString(lcid,xbStringNum_hexa)+"'","xbCfgEngine::GetNameFromLCID(), locale id not found");
		return xbString();
	}

	return (*ppItem)->m_name;
}

LCID xbCfgEngine::GetLCIDfromName(const xbString &lang,xbOpMode opm) const
{
	if (xbStricmp(lang,"system") == 0)
		return GetRealLCID(LOCALE_SYSTEM_DEFAULT,opm);
	if (xbStricmp(lang,"user") == 0)
		return GetRealLCID(LOCALE_USER_DEFAULT,opm);

	xbSectLock lck(&m_sectCache);
	if (m_arrLcid.IsEmpty())
		((xbCfgEngine*)this)->LCIDload();

	LCIDitem *const *ppItem = m_arrLcid.SortedFind((LPCSTR)lang,lcidFindNameCB);
	if (!ppItem)
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown lang '"+lang+"'","xbCfgEngine::GetLCIDfromName(), locale name not found");
		return 0;
	}

	return (*ppItem)->m_lcid;
}

LCID xbCfgEngine::GetRealLCID(LCID lcid,xbOpMode opm) const
{
	lcid =  ::ConvertDefaultLocale(lcid);
	if (!IsValidLCID(lcid))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Invalid or not supported locale id ("+xbString(lcid,xbStringNum_hexa)+")","xbCfgEngine::GetRealLCID()");
		return 0;
	}
	return lcid;
//	xbThrow xbEapp(E_NOTIMPL,"Can't retrieve real lcid, function not implemented","xbCfgEngine::GetRealLCID()");
}

bool xbCfgEngine::IsValidLCID(LCID lcid,bool bInstalled) const
{
	return ::IsValidLocale(lcid,bInstalled?LCID_INSTALLED:LCID_SUPPORTED)?true:false;
}

bool xbCfgEngine::IsValidLCID(const xbString &lcidName,bool bInstalled) const
{
	LCID lcid = GetLCIDfromName(lcidName,xbOp_Optional);
	if (!lcid)
		return false;
	return ::IsValidLocale(lcid,bInstalled?LCID_INSTALLED:LCID_SUPPORTED)?true:false;
}

bool xbCfgEngine::EnumLCID(int &idx, ulong *lcid, xbString *lcidName, xbString *lcidLabel,bool bInstalled)
{
	xbSectLock lck(&m_sectCache);
	if (m_arrLcid.IsEmpty())
		((xbCfgEngine*)this)->LCIDload();
	for(;;)
	{
		if (idx < 0)
			idx = 0;
		if (idx >= m_arrLcid.Length)
			return false;
		LCIDitem *pItem = m_arrLcid.GetAt(idx++);
		
		if (bInstalled && !IsValidLCID(pItem->m_lcid))
			continue;

		if (lcid)
			*lcid = pItem->m_lcid;
		if (lcidName)
			*lcidName = pItem->m_name;
		if (lcidLabel)
			*lcidLabel = pItem->m_label;
		return true;
	}
}

bool xbCfgEngine::GetAcceptedLanguages(xbString *pLng, xbOpMode opm) const
{
	//HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\International\AcceptLanguage
	xbODregKey key(xbODregCurrentUser,"Software\\Microsoft\\Internet Explorer\\International");
	if (!key.Exists() || !key.ValueExists("AcceptLanguage"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Accepted language information not found","xbCfgEngine::GetAcceptedLanguages()");
		return false;
	}
	if (pLng)
		*pLng = key.Value["AcceptLanguage"].AsString;
	return true;
}

bool xbCfgEngine::GetCurrentTimeZone(xbString *pName, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel, xbOpMode opm) const
{
	//Vista: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\TimeZoneKeyName
	//XP: HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Time Zones\TimeZoneKeyName
	if (pTZ && GetTimeZoneInformation(pTZ) == TIME_ZONE_ID_INVALID)
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Invalid time zone","xbCfgEngine::GetTimeZoneFromName(), failed to retrieve user info");
		return false;
	}
	if (pName || pLabel)
	{
		xbString name;
		xbODregKey key(xbODregLocalMachine,"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation");
		if (!key.Exists() || !key.ValueExists("TimeZoneKeyName"))
		{
			key.Init(xbODregCurrentUser,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
			if (!key.Exists() || !key.ValueExists("TimeZoneKeyName"))
			{
				if (opm == xbOp_Required)
					xbThrow xbEappCfg("Current time zone information not found","xbCfgEngine::GetTimeZoneFromName()");
				return false;
			}
		}
		name = key.Value["TimeZoneKeyName"].AsString;
		if (pName)
			*pName = name;
		if (pLabel)
		{
			key = xbODregKey(xbODregLocalMachine,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"+name);
			if (!key.Exists())
			{
				if (opm == xbOp_Required)
					xbThrow xbEappCfg("Unknown time zone '"+name+"'","xbCfgEngine::GetTimeZoneFromName(), time zone not found");
				return false;
			}

			*pLabel = key.Value["Display"].AsString;
		}
	}
	return true;
}
/*
typedef struct _TIME_ZONE_INFORMATION {
    LONG Bias;
    WCHAR StandardName[ 32 ];
    SYSTEMTIME StandardDate;
    LONG StandardBias;
    WCHAR DaylightName[ 32 ];
    SYSTEMTIME DaylightDate;
    LONG DaylightBias;
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION;
*/
struct shortTIME_ZONE_INFORMATION 
{
    LONG Bias;
    LONG StandardBias;
    LONG DaylightBias;
    SYSTEMTIME StandardDate;
    SYSTEMTIME DaylightDate;
};

bool xbCfgEngine::GetTimeZoneFromName(const xbString &name, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel, xbOpMode opm) const
{
	if (name.IsEmpty())
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp("Invalid time zone","xbCfgEngine::GetTimeZoneFromName(), extension is empty");
		return false;
	}
	xbString curName;
	if (pTZ && GetCurrentTimeZone(&curName,NULL,NULL,xbOp_Optional))
	{
		if (curName.CompareNoCase(name) == 0)
		{
			if (::GetTimeZoneInformation(pTZ) != TIME_ZONE_ID_INVALID)
			{
				pTZ = NULL;
				if (!pLabel)
					return true;
			}
		}
	}
/*	if (name.CompareNoCase("system")==0 || name.CompareNoCase("user")==0)
	{
		if (!pTZ)
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid time zone","xbCfgEngine::GetTimeZoneFromName(), argument pTZ is empty");
			return false;
		}
		if (GetTimeZoneInformation(pTZ) != TIME_ZONE_ID_INVALID)
		{
			pLabel->Empty();
			return true;
		}
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Invalid time zone","xbCfgEngine::GetTimeZoneFromName(), failed to retrieve user info");
		return false;
	}
*/
	xbODregKey key(xbODregLocalMachine,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"+name /*+" Standard Time"*/);
	if (!key.Exists())
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Unknown time zone '"+name+"'","xbCfgEngine::GetTimeZoneFromName(), time zone not found");
		return false;
	}
	if (!key.ValueExists("TZI"))
	{
		if (opm == xbOp_Required)
			xbThrow xbEappCfg("Invalid time zone '"+name+"'","xbCfgEngine::GetTimeZoneFromName(), TZI not found");
		return false;
	}
	if (pTZ)
	{
		xbData tzi;
		key.Value["TZI"].GetData(&tzi);
		for(;;)
		{
			if (tzi.Length == sizeof(shortTIME_ZONE_INFORMATION))
			{
				memset(pTZ,0,sizeof(TIME_ZONE_INFORMATION));

				const shortTIME_ZONE_INFORMATION *pShTZ = (const shortTIME_ZONE_INFORMATION*)(LPCVOID)tzi;
				pTZ->Bias = pShTZ->Bias;
				pTZ->DaylightBias = pShTZ->DaylightBias;
				pTZ->DaylightDate = pShTZ->DaylightDate;
				pTZ->StandardBias = pShTZ->StandardBias;
				pTZ->StandardDate = pShTZ->StandardDate;
				break;
			}
			if (tzi.Length == sizeof(TIME_ZONE_INFORMATION))
			{
				memcpy(pTZ,tzi,sizeof(TIME_ZONE_INFORMATION));
				break;
			}
			if (opm == xbOp_Required)
				xbThrow xbEappCfg("Invalid time zone '"+name+"'","xbCfgEngine::GetTimeZoneFromName(), TZI is invalid");
			return false;
		}
	}

	if (pLabel)
		*pLabel = key.Value["Display"].AsString;

	return true;
}

bool xbCfgEngine::EnumTimeZone(int &idx, xbString *pName, TIME_ZONE_INFORMATION *pTZ, xbString *pLabel)
{
	xbODregKey key(xbODregLocalMachine,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
	xbODregEnumKey enumKey(key); 
	xbODregKey ctKey;
	enumKey.setPosition(idx);

//	xbString stdTime(" Standard Time");
	for(;;)
	{
		if (!enumKey.Next(&ctKey))
			return false;

		idx = enumKey.getPosition();

		xbString name(ctKey.Name);
//		if (name.Length <= stdTime.Length)
//			continue;

//		if (xbStricmp(((LPCSTR)name)+(name.Length - stdTime.Length),stdTime) != 0)
//			continue;

		if (!ctKey.ValueExists("TZI"))
			continue;

		if (pName)
			*pName = name;

		if (pLabel)
			*pLabel = ctKey.Value["Display"].AsString;

		if (pTZ)
		{
			xbData tzi;
			key.Value["TZI"].GetData(&tzi);
			if (tzi.Length != sizeof(TIME_ZONE_INFORMATION))
				continue;

			memcpy(pTZ,tzi,sizeof(TIME_ZONE_INFORMATION));
		}

		return true;
	}

}

bool xbCfgEngine::IsValidTimeZone(const xbString &name) const
{
	if (name.IsEmpty())
		return false;
	xbODregKey key(xbODregLocalMachine,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"+name/*+" Standard Time"*/);
	return key.Exists();
}

//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

void xbCPconvert(ulong cpSrc,const xbStringA &src,ulong cpDst,xbStringA *pDst)
{
	if (cpSrc == cpDst
		|| g_xbCfgEngine.GetRealCP(cpSrc) == g_xbCfgEngine.GetRealCP(cpDst))
	{
		*pDst = src;
		return;
	}
	int lgWide = ::MultiByteToWideChar(cpSrc,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1)");
		pDst->Empty();
		return;
	}
	xbData wdta;
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)wdta.GetWritePtr(lgWide*sizeof(wchar)),lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1a)");

	int lgMbcs = ::WideCharToMultiByte(cpDst,0,(LPCWSTR)(LPCVOID)wdta,lgWide,NULL,0, NULL, NULL);
	if (lgMbcs <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1b)");
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,0,(LPCWSTR)(LPCVOID)wdta,lgWide,(LPSTR)(LPCSTR)*pDst,lgMbcs, NULL, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert()");
}

void xbCPconvert(ulong cpSrc,LPCSTR src,int lgsrc,ulong cpDst,xbStringA *pDst)
{
	if (cpSrc == cpDst
		|| g_xbCfgEngine.GetRealCP(cpSrc) == g_xbCfgEngine.GetRealCP(cpDst))
	{
		*pDst = xbString(src,lgsrc);
		return;
	}
	int lgWide = ::MultiByteToWideChar(cpSrc,0,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc>0)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(2)");
		pDst->Empty();
		return;
	}
	xbData wdta;
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)wdta.GetWritePtr(lgWide*sizeof(wchar)),lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(2a)");

	int lgMbcs = ::WideCharToMultiByte(cpDst,0,(LPCWSTR)(LPCVOID)wdta,lgWide,NULL,0, NULL, NULL);
	if (lgMbcs <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(2b)");
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,0,(LPCWSTR)(LPCVOID)wdta,lgWide,(LPSTR)(LPCSTR)*pDst,lgMbcs, NULL, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(2c)");
}

void xbCPconvert(LPCWSTR src,int lgsrc,ulong cpDst,xbStringA *pDst)
{
	int lgMbcs = ::WideCharToMultiByte(cpDst,0,src,lgsrc,NULL,0, NULL, NULL);
	if (lgMbcs <= 0)
	{
		if (lgsrc>0)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(3)");
		pDst->Empty();
		return;
	}
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,0,src,lgsrc,(LPSTR)(LPCSTR)*pDst,lgMbcs, NULL, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(3a)");
}

void xbCPconvert(ulong cpSrc,const xbStringA &src,xbBSTR *pDst)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(4)");
		pDst->Empty();
		return;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(4a)");
}

void xbCPconvert(ulong cpSrc,LPCSTR src,int lgsrc,xbBSTR *pDst)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(5)");
		pDst->Empty();
		return;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(5a)");
}

void xbCPconvert(ulong cpSrc,const xbStringA &src,xbStringW *pDst)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(4)");
		pDst->Empty();
		return;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(4a)");
}

void xbCPconvert(ulong cpSrc,LPCSTR src,int lgsrc,xbStringW *pDst)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(5)");
		pDst->Empty();
		return;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(5a)");
}


bool xbCPconvertEx(LPCWSTR src,int lgsrc,ulong cpDst,xbStringA *pDst,bool bForce,xbOpMode opm,LPCSTR lpDefaultChar)
{
	int lgMbcs = ::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,src,lgsrc,NULL,0, lpDefaultChar, NULL);
	if (lgMbcs <= 0)
	{
		if (lgsrc>0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(3)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,src,lgsrc,(LPSTR)(LPCSTR)*pDst,lgMbcs, lpDefaultChar, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(3a)");

	return true;
}

bool xbCPconvertEx(ulong cpSrc,const xbStringA &src,xbBSTR *pDst,bool bForce,xbOpMode opm)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(4)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(4a)");

	return true;
}

bool xbCPconvertEx(ulong cpSrc,LPCSTR src,int lgsrc,xbBSTR *pDst,bool bForce,xbOpMode opm)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(5)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(5a)");

	return true;
}


bool xbCPconvertEx(ulong cpSrc,const xbStringA &src,xbStringW *pDst,bool bForce,xbOpMode opm)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(4)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(4a)");

	return true;
}

bool xbCPconvertEx(ulong cpSrc,LPCSTR src,int lgsrc,xbStringW *pDst,bool bForce,xbOpMode opm)
{
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(5)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvertEx(5a)");

	return true;
}

bool xbCPconvertEx(ulong cpSrc,const xbStringA &src,ulong cpDst,xbStringA *pDst,bool bForce,xbOpMode opm,LPCSTR lpDefaultChar)
{
	if (cpSrc == cpDst
		|| g_xbCfgEngine.GetRealCP(cpSrc) == g_xbCfgEngine.GetRealCP(cpDst))
	{
		*pDst = src;
		return true;
	}
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && src.IsValid() && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,src.Length,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (src.IsValid())
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	xbData wdta;
	if (::MultiByteToWideChar(cpSrc,0,src,src.Length,(LPWSTR)wdta.GetWritePtr(lgWide*sizeof(wchar)),lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1a)");

	int lgMbcs = ::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,(LPCWSTR)(LPCVOID)wdta,lgWide,NULL,0, lpDefaultChar, NULL);
	if (lgMbcs <= 0)
	{
		if (opm == xbOp_Required)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1b)");
		return false;
	}
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,(LPCWSTR)(LPCVOID)wdta,lgWide,(LPSTR)(LPCSTR)*pDst,lgMbcs, lpDefaultChar, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert()");
	return true;
}

bool xbCPconvertEx(ulong cpSrc,LPCSTR src,int lgsrc,ulong cpDst,xbStringA *pDst,bool bForce,xbOpMode opm,LPCSTR lpDefaultChar)
{
	if (cpSrc == cpDst
		|| g_xbCfgEngine.GetRealCP(cpSrc) == g_xbCfgEngine.GetRealCP(cpDst))
	{
		*pDst = src;
		return true;
	}
	int lgWide = ::MultiByteToWideChar(cpSrc,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#ifdef xbCPCONVERT_UTF8TOLERANT
	if (opm == xbOp_Required && lgWide <= 0 && lgsrc>0 && cpSrc == CP_UTF8 && !g_xbCfgEngine.utf8strict())
		lgWide = ::MultiByteToWideChar(cpSrc = CP_ACP,bForce?0:MB_ERR_INVALID_CHARS,src,lgsrc,NULL,0);
#endif
	if (lgWide <= 0)
	{
		if (lgsrc>0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	xbData wdta;
	if (::MultiByteToWideChar(cpSrc,0,src,lgsrc,(LPWSTR)wdta.GetWritePtr(lgWide*sizeof(wchar)),lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1a)");

	int lgMbcs = ::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,(LPCWSTR)(LPCVOID)wdta,lgWide,NULL,0, lpDefaultChar, NULL);
	if (lgMbcs <= 0)
	{
		if (opm == xbOp_Required)
			xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert(1b)");
		return false;
	}
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::WideCharToMultiByte(cpDst,bForce?WC_DEFAULTCHAR:0,(LPCWSTR)(LPCVOID)wdta,lgWide,(LPSTR)(LPCSTR)*pDst,lgMbcs, lpDefaultChar, NULL) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPconvert()");
	return true;
}

bool xbCPtransform(LCID lcid,LPCWSTR src,int lgsrc,xbCPtransformMode mask,xbBSTR *pDst,xbOpMode opm)
{
	int lgWide = ::LCMapStringW(lcid,(DWORD)mask,src,lgsrc,NULL,0);
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(1)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs

	if (::LCMapStringW(lcid,(DWORD)mask,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(1a)");

	return true;
}


bool xbCPtransform(LCID lcid,LPCWSTR src,int lgsrc,xbCPtransformMode mask,xbStringW *pDst,xbOpMode opm)
{
	int lgWide = ::LCMapStringW(lcid,(DWORD)mask,src,lgsrc,NULL,0);
	if (lgWide <= 0)
	{
		if (lgsrc > 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(1)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgWide;//checked, zt not included in  lgMbcs

	if (::LCMapStringW(lcid,(DWORD)mask,src,lgsrc,(LPWSTR)(LPCWSTR)*pDst,lgWide) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(1a)");

	return true;
}

bool xbCPtransform(LCID lcid,LPCSTR src,int lgsrc,xbCPtransformMode mask,xbStringA *pDst,xbOpMode opm)
{
	int lgMbcs = ::LCMapStringA(lcid,(DWORD)mask,src,lgsrc,NULL,0);
	if (lgMbcs <= 0)
	{
		if (lgsrc > 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(2)");
			return false;
		}
		pDst->Empty();
		return true;
	}
	pDst->Length = lgMbcs;//checked, zt not included in  lgMbcs

	if (::LCMapStringA(lcid,(DWORD)mask,src,lgsrc,(LPSTR)(LPCSTR)*pDst,lgMbcs) <= 0)
		xbThrow xbEsys(xbHR_LAST, "string conversion api failure - xbCPtransform(2a)");

	return true;
}

xbString xbCfgEngine::GetExpandedString(const xbString &str)
{
	xbString ret,substr;
	int pos = 0;
	char chSep = 0,chSepPrev = 0;
	while((pos = str.GetToken(pos,"%",&substr,NULL,&chSep)) > 0)
	{
		if (chSepPrev == '%' && chSep == '%')
		{
			if (substr.CompareNoCase("system32") == 0 || substr.CompareNoCase("system") == 0)
			{
				ret += ::xbGetSystemDir(false);
				chSep = chSepPrev = 0;
				continue;
			}

			if (substr.CompareNoCase("systemroot") == 0 || substr.CompareNoCase("windir") == 0)
			{
				ret += ::xbGetWindowsDir(false);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("downloadedprogramfiles") == 0)
			{
				ret += ::xbGetWindowsDir(true)+"Downloaded Program Files";
				chSep = chSepPrev = 0;
				continue;
			}

			if (substr.CompareNoCase("username") == 0)
			{
				ret += ::xbGetUserName();
				chSep = chSepPrev = 0;
				continue;
			}

			if (substr.CompareNoCase("computername") == 0)
			{
				ret += ::xbGetComputerName();
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("programfiles") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_PROGRAM_FILES);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("xdcprogramfiles") == 0)
			{
				ret += ::xbGetXdcPrgDir(false);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("xdcprogramdata") == 0)
			{
				ret += ::xbGetXdcPrgDataDir(false);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("mydocuments") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_PERSONAL);
				chSep = chSepPrev = 0;
				continue;
			}			
			if (substr.CompareNoCase("mymusic") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_MYMUSIC);
				chSep = chSepPrev = 0;
				continue;
			}			
			if (substr.CompareNoCase("myvideo") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_MYVIDEO);
				chSep = chSepPrev = 0;
				continue;
			}			
			if (substr.CompareNoCase("localappdata") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_LOCAL_APPDATA);
				chSep = chSepPrev = 0;
				continue;
			}			
			if (substr.CompareNoCase("appdata") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_APPDATA);
				chSep = chSepPrev = 0;
				continue;
			}			
			if (substr.CompareNoCase("xdcprogramfilesMenu") == 0)
			{
				ret += ::xbGetXdcMenuDir(false);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("programfilesMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_COMMON_PROGRAMS);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("programfilesUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_PROGRAMS);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("startMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_COMMON_STARTMENU);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("startUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_STARTMENU);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("startUpMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_COMMON_STARTUP);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("startUpUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_STARTUP);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("desktop") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_COMMON_DESKTOPDIRECTORY);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("desktopUser") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_DESKTOP);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("FavoritesMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_COMMON_FAVORITES);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("FavoritesUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_FAVORITES);
				chSep = chSepPrev = 0;
				continue;
			}
			
			if (substr.CompareNoCase("RecentUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_RECENT);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("sendToUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_SENDTO);
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("quickLaunchUserMenu") == 0)
			{
				ret += ::xbGetShellFolder(CSIDL_APPDATA)+"\\Microsoft\\Internet Explorer\\Quick Launch";
				chSep = chSepPrev = 0;
				continue;
			}			
			
			if (substr.CompareNoCase("xdcsysdir") == 0)
			{
				ret += xbXDCSYSPATH;
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("xdclibdir") == 0)
			{
				ret += ::xbGetFileDir(g_xdcBaseDLL.GetInstance());
				chSep = chSepPrev = 0;
				continue;
			}

			if (substr.CompareNoCase("xdcappdir") == 0)
			{
				ret += ::xbGetFileDir();
				chSep = chSepPrev = 0;
				continue;
			}
			if (substr.CompareNoCase("xdcappname") == 0)
			{
				ret += ::xbGetFileName();
				chSep = chSepPrev = 0;
				continue;
			}
		}

		if (chSepPrev)
			ret += chSepPrev;
		if (substr.IsValid())
			ret += substr;
		chSepPrev = chSep;
	}
	if (chSep)
		ret += chSep;
	return ret;
}

#pragma warning(pop)

#endif
