/*
	CVSNT Automatic manifest generator
    Copyright (C) 2006 Tony Hoyle and March-Hare Software Ltd

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// mkmanifest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "../../version.h"
#include <atlbase.h> // Includes CComVariant and CComBSTR.

int parse_manifest(MSXML2::IXMLDOMDocument2Ptr outDoc, const char *xml)
{
	MSXML2::IXMLDOMDocument2Ptr doc;
	CComVariant varNodeType;
	HRESULT hr;
	
	varNodeType = NODE_ELEMENT;  // MSXML2::NODE_ELEMENT
	hr = doc.CreateInstance("Msxml2.DOMDocument.6.0");
	if (!SUCCEEDED(hr))
	{
		// the most common thing here is "invalid class string"
		hr = doc.CreateInstance("Msxml2.DOMDocument.5.0");
		if (!SUCCEEDED(hr))
		{
			hr = doc.CreateInstance("Msxml2.DOMDocument.4.0");
			if (!SUCCEEDED(hr))
			{
				return -1;
			}
		}
	}

	if(!doc->loadXML(xml))
		return -1;

	doc->setProperty("SelectionLanguage","XPath");
	doc->setProperty("SelectionNamespaces", "xmlns:ns='urn:schemas-microsoft-com:asm.v1'");

	_bstr_t version,name,type,processor,language;
	MSXML2::IXMLDOMNodePtr node,val;

	node = doc->selectSingleNode("/ns:assembly/ns:assemblyIdentity");
	if(node==NULL)
		return -1;
	val = node->attributes->getNamedItem("version");
	if(val) version = val->text;
	else return -1;

	val = node->attributes->getNamedItem("name");
	if(val) name = val->text;
	else return -1;

	val = node->attributes->getNamedItem("processorArchitecture");
	if(val) processor = val->text;
#ifdef _WIN64
	else processor="amd64";
#else
	else processor="x86";
#endif

	val = node->attributes->getNamedItem("language"); // No examples of this so this may be wrong
	if(val) language = val->text;
	else language="*";

	val = node->attributes->getNamedItem("type");
	if(val) type = val->text;
	else type="Win32";

	node = outDoc->selectSingleNode("/ns:assembly");
	val = outDoc->createNode(varNodeType,"dependency","urn:schemas-microsoft-com:asm.v1");
	node->appendChild(val);
	node = val;

	MSXML2::IXMLDOMNodePtr dep = outDoc->createNode(varNodeType,"dependentAssembly","urn:schemas-microsoft-com:asm.v1");
	MSXML2::IXMLDOMNodePtr iden = outDoc->createNode(varNodeType,"assemblyIdentity","urn:schemas-microsoft-com:asm.v1");

	node->appendChild(dep);
	dep->appendChild(iden);

	val = outDoc->createAttribute("version");
	val->text=version;
	iden->attributes->setNamedItem(val);

	val = outDoc->createAttribute("type");
	val->text=type;
	iden->attributes->setNamedItem(val);

	val = outDoc->createAttribute("name");
	val->text=name;
	iden->attributes->setNamedItem(val);

	val = outDoc->createAttribute("processorArchitecture");
	val->text=processor;
	iden->attributes->setNamedItem(val);

	val = outDoc->createAttribute("language");
	val->text=language;
	iden->attributes->setNamedItem(val);

	return 0;
}
 
static WORD g_wManifestLanguage=MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

BOOL CALLBACK EnumResLangProc(      
        HANDLE hModule,
        LPCTSTR lpszType,
        LPCTSTR lpszName,
        WORD wIDLanguage,
        LONG_PTR lParam)
{
	g_wManifestLanguage=wIDLanguage;
	return FALSE;
}

void mkmanifest_help(void)
{
	printf("cvsnt manifest patcher "CVSNT_PRODUCTVERSION_STRING"\n");
	printf("usage: mkmanifest [-v version] [-d] [-r] [-q] [-a] [-u] [-P searchpath] [-d] <library>\n");
	printf("   -d   verbose mode (implies -a -u)\n");
	printf("   -a   create an ascii manifest file\n");
	printf("   -u   create a unicode manifest file (with BOM)\n");
	printf("   -q   quiet and do not prompt for a key at end\n");
	printf("   -Q   really quiet and do not prompt for a key at end\n");
	printf("   **** experimental options:\n");
	printf("   -r   if there is no version block in the resource segment attempt to add one\n");
	printf("   -f   force mkmanifest to add a manifest with no version block\n");
}

int main(int argc, char* argv[])
{
	LOADED_IMAGE *loaded_image;
	char szPath[4096];
	std::string version,path;
	bool setversion=false;
	bool debug=false;
	bool addversion=false;
	bool asciimanifest=false;
	bool forceadd=false;
	bool unicodemanifest=false;
	bool donotstop=false;
	bool reallyquiet=false;
	HRESULT hr;

	hr = CoInitialize(NULL);
	if (hr != S_OK )
		return -1;

	MSXML2::IXMLDOMDocument2Ptr doc;

	hr = doc.CreateInstance("Msxml2.DOMDocument.6.0");
	if (!SUCCEEDED(hr))
	{
		// the most common thing here is "invalid class string"
		hr = doc.CreateInstance("Msxml2.DOMDocument.5.0");
		if (!SUCCEEDED(hr))
		{
			hr = doc.CreateInstance("Msxml2.DOMDocument.4.0");
			if (!SUCCEEDED(hr))
			{
				return -1;
			}
		}
	}
	version=CVSNT_PRODUCTVERSION_SHORT;
	path="..;d:\\cvsbin\\sysfiles;d:\\cvsbin;..\\cvsdeps\\sysfiles;c:\\Program Files\\CVSNT;c:\\Program Files\\CVS Suite\\CVSNT;c:\\Program Files\\CVSNT\\sysfiles;c:\\Program Files\\CVS Suite\\CVSNT\\sysfiles";
	debug=false;

	while(argc>1 && argv[1][0]=='-')
	{
		switch(argv[1][1])
		{
		case 'v':
			version = argv[2];
			argv++;
			argc--;
			setversion=true;
			break;
		case 'P':
			path = argv[2];
			argv++;
			argc--;
			break;
		case 'a':
			asciimanifest = true;
			break;
		case 'f':
			forceadd = true;
			break;
		case 'u':
			unicodemanifest = true;
			break;
		case 'd':
			debug = true;
			break;
		case 'q':
			donotstop = true;
			break;
		case 'Q':
			reallyquiet = true;
			break;
		case 'r':
			addversion = true;
			break;
		default:
			mkmanifest_help();
			return -1;
		}
		argv++;
		argc--;
	}

	if(argc<2)
	{
		mkmanifest_help();
		return -1;
	}

	HMODULE hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES);
	if(!hRes)
	{
		printf("Couldn't load image!\n");
		return 0;
	}

	GetModuleFileName(hRes, szPath, sizeof(szPath));

	if(debug)
		printf("Generating manifest for version %s of %s\n",version.c_str(),szPath);

	*strrchr(szPath,'\\')='\0';
	SetCurrentDirectory(szPath);

	strcpy(szPath,path.c_str());
	strcat(szPath,";");
	GetEnvironmentVariable("Path",szPath+strlen(szPath),(DWORD)(sizeof(path)-strlen(szPath)));
	SetEnvironmentVariable("Path",szPath);

	HRSRC hRsrc = FindResource(hRes,MAKEINTRESOURCE(1),RT_MANIFEST);
	if(hRsrc)
	{
		if(debug)
			printf("Found the manifest resource\n");

		EnumResourceLanguages(hRes,RT_MANIFEST,MAKEINTRESOURCE(1),(ENUMRESLANGPROC)EnumResLangProc,NULL);
		LPVOID pRsrc = LoadResource(hRes, hRsrc);
		DWORD dwLen = SizeofResource(hRes, hRsrc);
		DWORD dwOldProtect;
		VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect);
		((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need
		if(doc->loadXML((const char *)pRsrc))
		{
			doc->setProperty("SelectionLanguage","XPath");
			doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'");
		}
		else
		{
			const char *_xml="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
						"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
						"<assemblyIdentity version=\"1.0.0.0\" processorArchitecture=\"*\" name=\"\" type=\"win32\"/>\n"
						"</assembly>\n";
			if(doc->loadXML(_xml))
			{
				doc->setProperty("SelectionLanguage","XPath");
				doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'");
			}
		}
		FreeResource(hRsrc);
	}
	else
	{
		if(debug)
			printf("No manifest resource in this object - make our own\n");

		const char *_xml="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
					"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
					"<assemblyIdentity version=\"1.0.0.0\" processorArchitecture=\"*\" name=\"\" type=\"win32\"/>\n"
					"</assembly>\n";
		if(doc->loadXML(_xml))
		{
			doc->setProperty("SelectionLanguage","XPath");
			doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'");
		}
	}
	HRSRC hRsrc_v = FindResource(hRes,MAKEINTRESOURCE(VS_VERSION_INFO),RT_VERSION);
	if(hRsrc_v)
	{
		LPVOID pRsrc = LoadResource(hRes, hRsrc_v);
		// something here.. updating versioninfo is even harder than updating the manifests!
		if (pRsrc!=NULL)
		{
			if(debug)
				printf("The version resource is loaded \n");

			LPVOID pBlock = LockResource(pRsrc);  
		    if (pBlock != NULL)
		    {
				VS_FIXEDFILEINFO fixedinfo;
				const char csEntry[]="FileVersion\0";
				char csRet[4096];
				DWORD dwLen,langD;
				BOOL retVal;    
				LPVOID retbuffer=NULL;
				static char szSubBlock[256];

				sprintf(szSubBlock, "\\");
				retVal = VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen);
				if (retVal && dwLen==sizeof(fixedinfo))
				{
					memcpy(&fixedinfo,retbuffer,sizeof(fixedinfo));
					if ((!reallyquiet) || (debug))
					{
						printf("FileVersion = %d, %d, %d, %d\n",
							(fixedinfo.dwFileVersionMS&0xffff0000)>>16,
							fixedinfo.dwFileVersionMS&0xffff,
							(fixedinfo.dwFileVersionLS&0xffff0000)>>16,
							fixedinfo.dwFileVersionLS&0xffff);
						printf("ProductVersion = %d, %d, %d, %d\n",
							(fixedinfo.dwProductVersionMS&0xffff0000)>>16,
							fixedinfo.dwProductVersionMS&0xffff,
							(fixedinfo.dwProductVersionLS&0xffff0000)>>16,
							fixedinfo.dwProductVersionLS&0xffff);
						if (debug)
						{
							printf("FileOS = 0x%x\n",fixedinfo.dwFileOS);
							printf("FileType = 0x%x\n",fixedinfo.dwFileType);
							printf("FielSubtype = 0x%x\n",fixedinfo.dwFileSubtype);
						}
					}
				}

				sprintf(szSubBlock, "\\VarFileInfo\\Translation");
				retVal = VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen);
				if (retVal && dwLen==4)
				{
					memcpy(&langD, retbuffer, 4);
					sprintf(szSubBlock, "\\StringFileInfo\\%02X%02X%02X%02X\\%s",
                        (langD & 0xff00)>>8, langD & 0xff,(langD & 0xff000000)>>24,
						(langD & 0xff0000)>>16, csEntry);
				}
				else
					sprintf(szSubBlock, "\\StringFileInfo\\%04X04B0\\%s", 
						GetUserDefaultLangID(), csEntry);
				
				dwLen=0;
				if (VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen))
					strcpy(csRet, (char *)retbuffer);
				else
				{
					if (debug)
						printf("VerQueryValue returned error\n");
				}
				UnlockResource(pRsrc); 
				if (debug)
					printf("VerQueryValue %s = %s\n",szSubBlock,(char *)csRet);
			}
		}
		FreeResource(hRsrc_v);
	} 
	else
	{
		if ((!addversion)&&(!forceadd))
		{
			printf("The file does not contain any version resources, cannot add a manifest!\n");
			printf("Use the -r option if you want to attempt adding version resources.\n");
			printf("or use the -f option to force adding a manifest with no version resources.\n");
			return -1;
		}
		else 
		if (forceadd)
		{
			printf("The file does not contain any resources, adding a manifest will probably break it!\n");
		} 
		else
		{
			// attempt to add a version block!
			VS_FIXEDFILEINFO fixedinfo;
			size_t lenpath;
			char szPathFile[4096];
			GetModuleFileName(hRes, szPathFile, sizeof(szPathFile));

			if (setversion)
			{
				printf("Cannot combine -v and -r parameters!\n");
				return -1;
			}
			if (debug)
				printf("Attempt to add a version resource.\n");
			ZeroMemory(&fixedinfo,sizeof(fixedinfo));
			// CVSNT_PRODUCT_MAJOR,
			// CVSNT_PRODUCT_MINOR,
			// CVSNT_PRODUCT_PATCHLEVEL:
			// CVSNT_PRODUCT_BUILD:
			fixedinfo.dwFileVersionMS = CVSNT_PRODUCT_MAJOR<<16;
			fixedinfo.dwFileVersionMS += CVSNT_PRODUCT_MINOR;
			fixedinfo.dwFileVersionLS = CVSNT_PRODUCT_PATCHLEVEL<<16;
			fixedinfo.dwFileVersionLS += CVSNT_PRODUCT_BUILD;
			if (!reallyquiet) 
			{
				printf("FileVersion = %d, %d, %d, %d\n",
					(fixedinfo.dwFileVersionMS&0xffff0000)>>16,
					fixedinfo.dwFileVersionMS&0xffff,
					(fixedinfo.dwFileVersionLS&0xffff0000)>>16,
					fixedinfo.dwFileVersionLS&0xffff);
			}
			fixedinfo.dwProductVersionMS = CVSNT_PRODUCT_MAJOR<<16;
			fixedinfo.dwProductVersionMS += CVSNT_PRODUCT_MINOR;
			fixedinfo.dwProductVersionLS = CVSNT_PRODUCT_PATCHLEVEL<<16;
			if (!reallyquiet) 
			{
				printf("ProductVersion = %d, %d, %d, %d\n",
					(fixedinfo.dwProductVersionMS&0xffff0000)>>16,
					fixedinfo.dwProductVersionMS&0xffff,
					(fixedinfo.dwProductVersionLS&0xffff0000)>>16,
					fixedinfo.dwProductVersionLS&0xffff);
			}
			fixedinfo.dwFileFlags = 0x1;
			fixedinfo.dwFileFlagsMask = 0x17;
			fixedinfo.dwFileOS = 0x4; // VOS_WINDOWS32
			if (debug) 
			{
				printf("FileOS = 0x%x\n",fixedinfo.dwFileOS);
				printf("FileType = 0x%x\n",fixedinfo.dwFileType);
				printf("FielSubtype = 0x%x\n",fixedinfo.dwFileSubtype);
			}
			lenpath=strlen(argv[1]);
			if (debug)
				printf("the file suffiix is %s\n",&argv[1][lenpath-4]);
			if (stricmp(&argv[1][lenpath-4],".dll")==0)
				fixedinfo.dwFileType = 0x2; // VFT_DLL
			else
				fixedinfo.dwFileType = 0x1; // VFT_APP
			fixedinfo.dwFileSubtype = 0; // VFT2_UNKNOWN
			FreeLibrary(hRes);
			HANDLE hUpdate1 = BeginUpdateResource(argv[1],TRUE);
			if(!hUpdate1)
			{
				printf("BeginUpdateResource (version) failed!!!\n");
				return -1;
			}
			if(!UpdateResource(hUpdate1,RT_VERSION,"",g_wManifestLanguage,(LPVOID)&fixedinfo,sizeof(fixedinfo)))
			{
				printf("UpdateResource (version) failed!!\n");
				return -1;
			}
			if(!EndUpdateResource(hUpdate1, FALSE))
			{
				printf("EndUpdateResource (version) failed!!\n");
				return -1;
			}
			hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES);
			if (hRes==NULL)
				return -1;
		}
	}

	loaded_image=ImageLoad(argv[1],NULL);
	if(!loaded_image)
	{
		printf("Couldn't load image!");
		return 0;
	}

	DWORD	dwSize;
	PBYTE      pDllName;

	PIMAGE_IMPORT_DESCRIPTOR pimage_import_descriptor= (PIMAGE_IMPORT_DESCRIPTOR)
		ImageDirectoryEntryToData(loaded_image->MappedAddress,FALSE,IMAGE_DIRECTORY_ENTRY_IMPORT,&dwSize);

	if(debug) printf("Dependencies:\n");
	while(pimage_import_descriptor->Name!=0)
	{
		pDllName = (PBYTE)ImageRvaToVa(loaded_image->FileHeader,loaded_image->MappedAddress,pimage_import_descriptor->Name,NULL);

		HMODULE hRes2 = LoadLibraryEx((LPCSTR)pDllName,NULL,DONT_RESOLVE_DLL_REFERENCES);
		if(hRes2)
		{
			GetModuleFileName(hRes2, szPath, sizeof(szPath));
			HRSRC hRsrc = FindResource(hRes2,MAKEINTRESOURCE(1),RT_MANIFEST);
			if(hRsrc)
			{
				LPVOID pRsrc = LoadResource(hRes2, hRsrc);
				DWORD dwLen = SizeofResource(hRes2, hRsrc);
				DWORD dwOldProtect;
				VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect);
				((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need
				if(debug) printf("    %s (manifest)\n",szPath);
				parse_manifest(doc, (const char *)pRsrc);
				FreeResource(hRsrc);
			}
			else
				if(debug) printf("    %s\n",szPath);
			FreeLibrary(hRes2);
		}

		pimage_import_descriptor++;
	}

	ImgDelayDescr *pimage_delay_descriptor= (ImgDelayDescr*)
		ImageDirectoryEntryToData(loaded_image->MappedAddress,FALSE,IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,&dwSize);

#if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1)
	if(debug) printf("Delayload dependencies (delayload version >= 0x0200 or delay imp ver >1):\n");
	while(pimage_delay_descriptor && pimage_delay_descriptor->rvaDLLName!=0)
#else
	if(debug) printf("Delayload dependencies (delayload version < 0x0200 and delay imp ver <1):\n");
	while(pimage_delay_descriptor && pimage_delay_descriptor->szName!=NULL)
#endif
	{
#if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1)
		pDllName = (PBYTE)ImageRvaToVa(loaded_image->FileHeader,loaded_image->MappedAddress,pimage_delay_descriptor->rvaDLLName,NULL);
#else
		pDllName = (PBYTE)pimage_delay_descriptor->szName;
#endif

		HMODULE hRes3 = LoadLibraryEx((LPCSTR)pDllName,NULL,DONT_RESOLVE_DLL_REFERENCES);
		if(hRes3)
		{
			GetModuleFileName(hRes3, szPath, sizeof(szPath));
			HRSRC hRsrc = FindResource(hRes3,MAKEINTRESOURCE(1),RT_MANIFEST);
			if(hRsrc)
			{
				LPVOID pRsrc = LoadResource(hRes3, hRsrc);
				DWORD dwLen = SizeofResource(hRes3, hRsrc);
				DWORD dwOldProtect;
				VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect);
				((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need
				if(debug) printf("    %s (manifest)\n",szPath);
				parse_manifest(doc, (const char *)pRsrc);
				FreeResource(hRsrc);
			}
			else
				if(debug) printf("    %s\n",szPath);
			FreeLibrary(hRes3);
		}

	    pimage_delay_descriptor++;
	}

	MSXML2::IXMLDOMNodePtr node,val;
	std::string name,arch;
	size_t off;
	const char *pname;

	pname = strrchr(loaded_image->ModuleName,'\\');
	if(!pname) pname = strrchr(loaded_image->ModuleName,'/');
	if(!pname) pname=loaded_image->ModuleName;
	else pname++;
	name = pname;
	off = name.find_last_of('.');
	if(off!=std::string::npos)
		name.resize(off);

#ifdef _WIN64
	arch="amd64";
#else
	arch="x86";
#endif

	node = doc->selectSingleNode("/ns:assembly/ns:assemblyIdentity");
	if(node==NULL)
	{
		printf("processing error: no assemblyIdentity\n");
		return -1;
	}
	val = node->attributes->getNamedItem("version");
	if(val) val->text=version.c_str();
	else
	{
		val = doc->createAttribute("version");
		val->text=version.c_str();
		node->attributes->setNamedItem(val);
	}
	val = node->attributes->getNamedItem("name");
	if(val) val->text=name.c_str();
	else
	{
		val = doc->createAttribute("name");
		val->text=name.c_str();
		node->attributes->setNamedItem(val);
	}
	val = node->attributes->getNamedItem("processorArchitecture");
	if(val) val->text=arch.c_str();
	else
	{
		val = doc->createAttribute("processorArchitecture");
		val->text=arch.c_str();
		node->attributes->setNamedItem(val);
	}

	ImageUnload(loaded_image);

	const char *_xsl = 
			"<xsl:stylesheet version=\"1.0\"\n"
			"	xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
			"<xsl:output method=\"xml\"/>\n"
			"<xsl:param name=\"indent-increment\" select=\"'   '\" />\n"
			"\n"
			"<xsl:template match=\"*\">\n"
			"	<xsl:param name=\"indent\" select=\"'&#xA;'\"/>\n"
			"\n"
			"	<xsl:value-of select=\"$indent\"/>\n"
			"	<xsl:copy>\n"
			"		<xsl:copy-of select=\"@*\" />\n"
			"		<xsl:apply-templates>\n"
			"			<xsl:with-param name=\"indent\"\n"
			"				select=\"concat($indent, $indent-increment)\"/>\n"
			"		</xsl:apply-templates>\n"
			"		<xsl:value-of select=\"$indent\"/>\n"
			"   </xsl:copy>\n"
			"</xsl:template>\n"
			"\n"
			"<xsl:template match=\"comment()|processing-instruction()\">\n"
			"	<xsl:copy />\n"
			"</xsl:template>\n"
			" \n"
			"<!-- WARNING: this is dangerous. Handle with care -->\n"
			"<xsl:template match=\"text()[normalize-space(.)='']\"/>\n"
			"\n"
			"</xsl:stylesheet>\n";

	MSXML2::IXMLDOMDocument2Ptr xslDoc;
	hr = xslDoc.CreateInstance("Msxml2.DOMDocument.6.0");
	if (!SUCCEEDED(hr))
	{
		// the most common thing here is "invalid class string"
		hr = xslDoc.CreateInstance("Msxml2.DOMDocument.5.0");
		if (!SUCCEEDED(hr))
		{
			hr = xslDoc.CreateInstance("Msxml2.DOMDocument.4.0");
			if (!SUCCEEDED(hr))
			{
				if(debug) printf("Cannot find MSXML 4.0, 5.0 or 6.0 installed\n");
				return -1;
			}
			else 
				if(debug) printf("Using MSXML 4.0\n");
		}
		else 
			if(debug) printf("Using MSXML 5.0\n");
	} 
	else 
		if(debug) printf("Using MSXML 6.0\n");
	xslDoc->loadXML(_xsl);
	_bstr_t txt = doc->transformNode(xslDoc);
	DWORD txt_length_in_bytes;

	txt_length_in_bytes=txt.length()*2;
	if(debug)
	{
		printf("\n");
		printf("text is %d characters long (%d bytes)\n",(int)txt.length(),(int)txt_length_in_bytes);
		printf("%s\n",(const char *)txt);
	}

	GetModuleFileName(hRes,szPath,MAX_PATH);
	FreeLibrary(hRes);

	HANDLE hUpdate = BeginUpdateResource(szPath,FALSE);
	if(!hUpdate)
	{
		printf("BeginUpdateResource failed!!!\n");
		return -1;
	}
	if (debug||asciimanifest||unicodemanifest)
	{
		DWORD written=0;
		HANDLE hFile;
		char manifest[4096], *lastdot;

		if (unicodemanifest || debug)
		{
			strcpy(manifest,szPath);
			if ((lastdot=strrchr(manifest,'.'))!=NULL)
				strcpy(lastdot,".manifest");
			else
				strcat(manifest,".manifest");
			hFile=CreateFile(manifest,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
			if (hFile!=INVALID_HANDLE_VALUE)
			{
				char szBOM[] = "\377\376\0";  // 0xFF, 0xFE  // leave off TEXT() macro.
				/* file begins with FF FE or FE FF (Reversed Byte Order Mark) */
			
				// this will write a UNICODE file:
				WriteFile(hFile,(LPVOID)szBOM,2,&written,NULL);
				WriteFile(hFile,(LPVOID)(const wchar_t *)txt,txt_length_in_bytes,&written,NULL);

				CloseHandle(hFile);
				if (debug) printf("Unicode manifest successfully written to %s!\n",manifest);
			}
		}
		if (asciimanifest || debug)
		{
			strcpy(manifest,szPath);
			if ((lastdot=strrchr(manifest,'.'))!=NULL)
				strcpy(lastdot,"-txt.manifest");
			else
				strcat(manifest,"-txt.manifest");
			hFile=CreateFile(manifest,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
			if (hFile!=INVALID_HANDLE_VALUE)
			{
				WriteFile(hFile,(LPVOID)(const char *)txt,txt.length(),&written,NULL);
				CloseHandle(hFile);
				if (debug) printf("ASCII manifest successfully written to %s!\n",manifest);
			}
		}
	}
	if(!UpdateResource(hUpdate,RT_MANIFEST,MAKEINTRESOURCE(1),g_wManifestLanguage,(LPVOID)(const char *)txt,txt.length()))
	{
		printf("UpdateResource failed!!\n");
		return -1;
	}
	else 
		if(debug) printf("UpdateResource succeeded!\n");
	if(!EndUpdateResource(hUpdate, FALSE))
	{
		printf("EndUpdateResource failed!!\n");
		return -1;
	}
	else 
		if(debug) printf("EndUpdateResource succeeded!\n");

	hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES);
	if (hRes==NULL)
		return -1;
	if (!donotstop)
	{
		if(debug) printf("Press a key to exit\n");
		getchar();
	}
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1