/*<<<***************************************************************************
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: xbVersion
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbVersion.h"
#include "xbFileTools.h"

bool	xbGetVersionInfo(LPCSTR pszFile,xbData *pData)
{
	xbString fname;
	if (!pszFile)
	{
		fname = xbGetFilePath();
		if (fname.IsEmpty()) return false;
		pszFile = fname;
	}
	DWORD nReserved=0;
	DWORD sz = ::GetFileVersionInfoSize((LPSTR)pszFile,&nReserved);
	if (!sz) return false;
	if (!pData) return true;

	if (!::GetFileVersionInfo((LPSTR)pszFile,nReserved,sz,pData->GetWritePtr(sz)))
		xbThrowEsys(xbHR_LAST,"GetFileVersionInfo() : failure");

	return true;
}

xbString xbGetProductName(LPCSTR pszFile,bool bWithVersion)
{
	xbData dta;
	if (!xbGetVersionInfo(pszFile,&dta))
		return xbGetFileName(pszFile);

	VS_FIXEDFILEINFO	*pFileInfo = xbGetVersionRoot(dta);

	xbString langcodepage(xbGetVersionLangCodePage(dta));
	xbString name(xbGetVersionString(dta,langcodepage,"ProductName"));

	if (name.IsEmpty()) name = xbGetFileName(pszFile);

	if (!bWithVersion) return name;

	if (pFileInfo)
	{
		char buf[128];
		_snprintf(buf,sizeof(buf)," %d.%d.%d.%d",((pFileInfo->dwProductVersionMS & 0xFFFF0000) >> 16)
			,(pFileInfo->dwProductVersionMS & 0xFFFF)
			,((pFileInfo->dwProductVersionLS & 0xFFFF0000) >> 16)
			,(pFileInfo->dwProductVersionLS & 0xFFFF));
		name += buf; 
	}
#ifdef _DEBUG
	name += "[DBG]";
#endif
#ifdef _RELDBG
	name += "[RDB]";
#endif
#ifdef _RELALT
	name += "[ALT]";
#endif
#ifdef	XDCBASE_X64
	name += "[x64]";
#endif
	return name;
}

extern xbVersion xbEXPORT xbGetProductVersion(LPCSTR pszFile,xbOpMode opm)
{
	xbData dta;
	if (!xbGetVersionInfo(pszFile,&dta))
	{
		if (opm == xbOp_Required) xbThrowEsys("xbGetProductVersion("+xbString(pszFile)+") : file doesn't have version");
		return xbVersion();
	}

	VS_FIXEDFILEINFO	*pFileInfo = xbGetVersionRoot(dta);
	if (!pFileInfo) return xbVersion();
	return xbVersion(*pFileInfo,xbVersion_Product);
}

extern xbVersion xbEXPORT xbGetFileVersion(LPCSTR pszFile,xbOpMode opm)
{
	xbData dta;
	if (!xbGetVersionInfo(pszFile,&dta))
	{
		if (opm == xbOp_Required) xbThrowEsys("xbGetFileVersion("+xbString(pszFile)+") : file doesn't have version");
		return xbVersion();
	}

	VS_FIXEDFILEINFO	*pFileInfo = xbGetVersionRoot(dta);
	if (!pFileInfo) return xbVersion();
	return xbVersion(*pFileInfo,xbVersion_File);
}

VS_FIXEDFILEINFO	*xbGetVersionRoot(const xbData &dta,UINT *pSz)
{
	VS_FIXEDFILEINFO	*pFileInfo;
	UINT				FileInfoSz = 0;
	if (!VerQueryValue((LPVOID)(LPCVOID(dta)),"\\",(void**)&pFileInfo,&FileInfoSz))
		pFileInfo = NULL;
	if (pSz) *pSz = FileInfoSz;

	return pFileInfo;
}


xbString			xbGetVersionLangCodePage(const xbData &dta,LPCSTR pDflt)
{
	BYTE	*pTrans;
	UINT	TransLen;
   //pTrans is a pointer to four 4 bytes of Hex number,
   //first two bytes are language id, and last two bytes are code
   //page. However, function return needs a  string of
   //4 hex digits, the first two characters correspond to the
   //language id and last two the last two character correspond
   //to the code page id.
	if (!VerQueryValue((LPVOID)(LPCVOID(dta)), "\\VarFileInfo\\Translation", (void**)&pTrans, &TransLen)
		|| TransLen < 4)
		return pDflt?pDflt:"";


    //now we change the order of the language id and code page
    //and convert it into a string representation.
    //For example, it may look like 040904E4
    //Or to pull it all apart:
    //04------        = SUBLANG_ENGLISH_USA
    //--09----        = LANG_ENGLISH
    // ----04E4 = 1252 = Codepage for Windows:Multilingual
    DWORD	HexNumber = DWORD(pTrans[2]) + (DWORD(pTrans[3]) << 8)
		+ (DWORD(pTrans[0]) << 16)
		+ (DWORD(pTrans[1]) << 24);

	char buf[32];
	_snprintf(buf,sizeof(buf),"%08X",HexNumber);

	return buf;
}

xbString			xbGetVersionString(const xbData &dta,const xbString &langcodepage,const xbString &stringName)
{
	CHAR				*pFileName;
	UINT				FileNameSz;
	xbString			key("\\StringFileInfo\\"+langcodepage+"\\"+stringName);
	if (!VerQueryValue((LPVOID)(LPCVOID(dta)),LPSTR(LPCSTR(key)),
		(void**)&pFileName,&FileNameSz))
		return "";

	return pFileName;
}

bool xbVersion::FromStr(const xbString &str,xbOpMode opm)
{
	const char *ptr = str;
	char *pEnd = NULL;
	int nb1 = strtol(ptr,&pEnd,10);
	if (!pEnd || (*pEnd != ',' && *pEnd != '.')) 
	{
		if (opm == xbOp_Required) xbThrowEsys("xbVersion::xbVersion() : '"+str+"' is an invalid version string (format: x.x.x.x or x,x,x,x)");
		return false;
	}
	ptr = pEnd+1;
	pEnd = NULL;
	int nb2 = strtol(ptr,&pEnd,10);
	if (!pEnd || (*pEnd != ',' && *pEnd != '.')) 
	{
		if (opm == xbOp_Required) xbThrowEsys("xbVersion::xbVersion() : '"+str+"' is an invalid version string (format: x.x.x.x or x,x,x,x)");
		return false;
	}
	ptr = pEnd+1;
	pEnd = NULL;
	int nb3 = strtol(ptr,&pEnd,10);
	if (!pEnd || (*pEnd != ',' && *pEnd != '.')) 
	{
		if (opm == xbOp_Required) xbThrowEsys("xbVersion::xbVersion() : '"+str+"' is an invalid version string (format: x.x.x.x or x,x,x,x)");
		return false;
	}
	ptr = pEnd+1;
	pEnd = NULL;
	int nb4 = strtol(ptr,&pEnd,10);
	if (pEnd && *pEnd != 0) 
	{
		if (opm == xbOp_Required) xbThrowEsys("xbVersion::xbVersion() : '"+str+"' is an invalid version string (format: x.x.x.x or x,x,x,x)");
		return false;
	}
	m_highVer = ((DWORD(nb1) & 0xFFFF) << 16) + (DWORD(nb2) & 0xFFFF);
	m_lowVer = ((DWORD(nb3) & 0xFFFF) << 16) + (DWORD(nb4) & 0xFFFF);
	return true;
}

int xbVersion::Compare(const xbVersion &ver) const
{
	if (m_highVer > ver.m_highVer) return 1;
	if (m_highVer < ver.m_highVer) return -1;
	if (m_lowVer > ver.m_lowVer) return 1;
	if (m_lowVer < ver.m_lowVer) return -1;
	return 0;
}

xbString xbVersion::ToStr(char chSep,bool bDisplayAny) const
{
	if (!bDisplayAny && IsEmpty()) 
		return xbString();

	char buf[128];
	_snprintf(buf,sizeof(buf),"%u%c%u%c%u%c%u",
		uint(ushort((m_highVer & 0xFFFF0000) >> 16)),chSep
		,uint(ushort(m_highVer & 0xFFFF)),chSep
		,uint(ushort((m_lowVer & 0xFFFF0000) >> 16)),chSep
		,uint(ushort(m_lowVer & 0xFFFF)));
	return buf;
}

