From 978ac79a5d062460fe06a04e8d313b0233e3a9e8 Mon Sep 17 00:00:00 2001 From: anders_k Date: Sun, 10 Feb 2019 20:45:40 +0000 Subject: [PATCH] Added PEAddResource and PERemoveResource git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7079 212acab6-be3b-0410-9dea-997c60f758d6 --- Docs/src/attributes.but | 18 ++ Docs/src/history.but | 2 + Source/BinInterop.cpp | 39 ++++ Source/BinInterop.h | 21 +++ Source/Platform.h | 14 +- Source/ResourceEditor.cpp | 305 +++++++++++++++++++++++++++++--- Source/ResourceEditor.h | 42 ++++- Source/Tests/ResourceEditor.cpp | 32 +++- Source/Tests/SConscript | 2 + Source/build.cpp | 6 +- Source/icon.cpp | 31 ++-- Source/icon.h | 4 +- Source/script.cpp | 61 ++++++- Source/tokens.cpp | 4 +- Source/tokens.h | 2 + Source/util.cpp | 80 +++------ Source/util.h | 1 + 17 files changed, 561 insertions(+), 103 deletions(-) diff --git a/Docs/src/attributes.but b/Docs/src/attributes.but index b100e9b1..eac5e351 100644 --- a/Docs/src/attributes.but +++ b/Docs/src/attributes.but @@ -327,6 +327,24 @@ Accepts variables. If variables are used, they must be initialized in \R{oninit} Specifies the output file that the MakeNSIS should write the installer to. This is just the file that MakeNSIS writes, it doesn't affect the contents of the installer. +\S2{apeaddresource} PEAddResource + +\c [/OVERWRITE|/REPLACE] file restype resname [reslang] + +Adds \cw{file} as a resource to the installer and uninstaller. \cw{restype} specifies the resource type and can be any string or # followed by a standard type or number. \cw{resname} must be # followed by a number. \cw{reslang} is optional and specifies the language id of the resource. Replacing standard NSIS resource are not supported, you should use \R{aicon}{Icon} and \R{achangeui}{ChangeUI} instead. + +\c PEAddResource "myimage.bmp" "#2" "#1337" +\c PEAddResource "mybonus.ico" "#Icon" "#200" +\c PEAddResource "myimage.png" "PNG" "#1234" + +\S2{aperemoveresource} PERemoveResource + +\c [/NOERRORS] restype resname reslang|ALL + +Removes a resource added with \cw{PEAddResource}. + +\c PERemoveResource "#Icon" "#200" ALL + \S2{arequestexecutionlevel} RequestExecutionLevel \c none|user|highest|\\admin\\ diff --git a/Docs/src/history.but b/Docs/src/history.but index d2534323..2ef29cd4 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -8,6 +8,8 @@ Released on ? ?th, 201? \S2{} Minor Changes +\b Added \R{apeaddresource}{PEAddResource} and \R{aperemoveresource}{PERemoveResource} + \b Added \R{loadandsetimage}{LoadAndSetImage} \S2{} Translations diff --git a/Source/BinInterop.cpp b/Source/BinInterop.cpp index 79abcb6a..474e0b24 100644 --- a/Source/BinInterop.cpp +++ b/Source/BinInterop.cpp @@ -26,6 +26,7 @@ #define LE2HE16 FIX_ENDIAN_INT16 // Little-endian 2 Host-endian #define LE2HE32 FIX_ENDIAN_INT32 #define HE2LE16 LE2HE16 +#define HE2BE32 BE2HE32 const size_t invalid_res_id = ~(size_t)0; FILE* MSTLB_fopen(const TCHAR*filepath, size_t*pResId) @@ -444,3 +445,41 @@ bool GetDLLVersion(const TCHAR *filepath, DWORD &high, DWORD &low) if (!result) result = GetDLLVersionFromVXD(filepath, high, low); return result; } + +DWORD GetDIBHeaderInfo(const void*pData, size_t DataSize, GENERICIMAGEINFO&Info) +{ + DWORD *p32 = (DWORD*) pData; + WORD *p16 = (WORD*) pData; + if (DataSize >= 12) + { + DWORD size = LE2HE32(p32[0]); + if (size == 12) // BITMAPCOREHEADER + { + Info.Width = LE2HE16(p16[2]), Info.Height = (INT32) (SHORT) LE2HE16(p16[3]); + Info.Planes = LE2HE16(p16[4]), Info.BPP = LE2HE16(p16[5]); + return size; + } + if (size >= 16) // OS22XBITMAPHEADER/BITMAPINFOHEADER/BITMAPV*HEADER + { + Info.Width = LE2HE32(p32[1]), Info.Height = (INT32) LE2HE32(p32[2]); + Info.Planes = LE2HE16(p16[6+0]), Info.BPP = LE2HE16(p16[6+1]); + return size; + } + } + return 0; +} + +DWORD IsBMPFile(const void*pData, size_t DataSize, GENERICIMAGEINFO*pInfo) +{ + BYTE *p8 = (BYTE*) pData; + if (DataSize >= 14+12 && p8[0] == 'B' && p8[1] == 'M') + { + DWORD *p32 = (DWORD*) (&p8[2]), fsize = LE2HE32(p32[0]), bitsoffs = LE2HE32(p32[2]); + if ((!fsize || fsize > 14+12) && (!bitsoffs || bitsoffs > 14+12)) + { + GENERICIMAGEINFO info; + return GetDIBHeaderInfo(p8 + 14, DataSize - 14, pInfo ? *pInfo : info); + } + } + return 0; +} diff --git a/Source/BinInterop.h b/Source/BinInterop.h index 9373288f..657a4d02 100644 --- a/Source/BinInterop.h +++ b/Source/BinInterop.h @@ -27,4 +27,25 @@ bool GetTLBVersion(const TCHAR *filepath, DWORD &high, DWORD &low); bool GetDLLVersion(const TCHAR *filepath, DWORD &high, DWORD &low); +typedef struct { + UINT32 Width, Height; + WORD BPP, Planes; +} GENERICIMAGEINFO; + +DWORD GetDIBHeaderInfo(const void*pData, size_t DataSize, GENERICIMAGEINFO&Info); +DWORD IsBMPFile(const void*pData, size_t DataSize, GENERICIMAGEINFO*pInfo = 0); + +inline WORD IsICOCURFile(const void*pData) +{ + WORD *p16 = (WORD*) pData, ico = 1, cur = 2, type, count; + if (p16[0] == FIX_ENDIAN_INT16(0x0000)) + if ((type = FIX_ENDIAN_INT16(p16[1])) == ico || type == cur) + return count = FIX_ENDIAN_INT16(p16[2]); + return 0; +} +inline WORD IsICOCURFile(const void*pData, size_t DataSize) +{ + return DataSize >= 6 ? IsICOCURFile(pData) : 0; +} + #endif //~ NSIS_BININTEROP_H diff --git a/Source/Platform.h b/Source/Platform.h index 495f70f7..9fb3529e 100644 --- a/Source/Platform.h +++ b/Source/Platform.h @@ -149,12 +149,14 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG; # define FIX_ENDIAN_INT32(x) (x) # define FIX_ENDIAN_INT16_INPLACE(x) ((void)(x)) # define FIX_ENDIAN_INT16(x) (x) +# define BE2HE32(x) SWAP_ENDIAN_INT32(x) #else # define FIX_ENDIAN_INT64(x) SWAP_ENDIAN_INT64(x) # define FIX_ENDIAN_INT32_INPLACE(x) ((x) = SWAP_ENDIAN_INT32(x)) # define FIX_ENDIAN_INT32(x) SWAP_ENDIAN_INT32(x) # define FIX_ENDIAN_INT16_INPLACE(x) ((x) = SWAP_ENDIAN_INT16(x)) # define FIX_ENDIAN_INT16(x) SWAP_ENDIAN_INT16(x) +# define BE2HE32(x) (x) #endif #define SWAP_ENDIAN_INT64(x) ( \ (((x)&0xFF00000000000000) >> 56) | \ @@ -230,7 +232,11 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG; # define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i)))) # endif # ifndef MAKEINTRESOURCE -# define MAKEINTRESOURCE MAKEINTRESOURCEA +# ifdef UNICODE +# define MAKEINTRESOURCE MAKEINTRESOURCEW +# else +# define MAKEINTRESOURCE MAKEINTRESOURCEA +# endif # endif # ifndef IMAGE_FIRST_SECTION # define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (ULONG_PTR) h + \ @@ -520,6 +526,10 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG; // resources +#ifndef RT_CURSOR +# define RT_CURSOR MAKEINTRESOURCE(1) +# define RT_GROUP_CURSOR MAKEINTRESOURCE(1 + 11) +#endif #ifndef RT_BITMAP # define RT_BITMAP MAKEINTRESOURCE(2) #endif @@ -530,7 +540,7 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG; # define RT_DIALOG MAKEINTRESOURCE(5) #endif #ifndef RT_GROUP_ICON -# define RT_GROUP_ICON MAKEINTRESOURCE(14) +# define RT_GROUP_ICON MAKEINTRESOURCE(3 + 11) #endif #ifndef RT_VERSION # define RT_VERSION MAKEINTRESOURCE(16) diff --git a/Source/ResourceEditor.cpp b/Source/ResourceEditor.cpp index 0e5b6cdd..4fa0b95a 100644 --- a/Source/ResourceEditor.cpp +++ b/Source/ResourceEditor.cpp @@ -20,8 +20,10 @@ #include "util.h" #include "winchar.h" #include +#include #include "tchar.h" #include "utf.h" +#include "BinInterop.h" using namespace std; ////////////////////////////////////////////////////////////////////// @@ -29,16 +31,11 @@ using namespace std; ////////////////////////////////////////////////////////////////////// #define WCHARPTR2WINWCHARPTR(s) ( (WINWCHAR*) (s) ) // Only for WinSDK structs like IMAGE_RESOURCE_DIR_STRING_U where we cannot change the WCHAR type! -#define ALIGN(dwToAlign, dwAlignOn) dwToAlign = (dwToAlign%dwAlignOn == 0) ? dwToAlign : dwToAlign - (dwToAlign%dwAlignOn) + dwAlignOn #define RALIGN(dwToAlign, dwAlignOn) ((dwToAlign%dwAlignOn == 0) ? dwToAlign : dwToAlign - (dwToAlign%dwAlignOn) + dwAlignOn) +#define ALIGN(dwToAlign, dwAlignOn) dwToAlign = RALIGN((dwToAlign), (dwAlignOn)) -static inline DWORD ConvertEndianness(DWORD d) { - return FIX_ENDIAN_INT32(d); -} - -static inline WORD ConvertEndianness(WORD w) { - return FIX_ENDIAN_INT16(w); -} +static inline DWORD ConvertEndianness(DWORD d) { return FIX_ENDIAN_INT32(d); } +static inline WORD ConvertEndianness(WORD w) { return FIX_ENDIAN_INT16(w); } PIMAGE_NT_HEADERS CResourceEditor::GetNTHeaders(BYTE* pbPE) { // Get dos header @@ -110,6 +107,74 @@ PRESOURCE_DIRECTORY CResourceEditor::GetResourceDirectory( return PRESOURCE_DIRECTORY(pbPE + dwResSecPtr); } +static int ParseSimpleInt(const TCHAR*parse, int*success) +{ + int base = 0, num; + TCHAR *end; + if (parse[0] == _T('0') && (parse[1] >= _T('0') && parse[1] <= _T('9'))) base = 10; // Avoid evil octal + num = (int)_tcstoul(parse, &end, base); + if (success) *success = !(int)(*end); + return num; +} + +const TCHAR* CResourceEditor::ParseResourceTypeString(const TCHAR*Str) +{ + if (Str[0] == _T('#')) // Special character used by KERNEL32!FindResource + { + ++Str; + if (!_tcsicmp(Str, _T("Bitmap"))) return (TCHAR*) RT_BITMAP; + if (!_tcsicmp(Str, _T("IconImage"))) return (TCHAR*) RT_ICON; + if (!_tcsicmp(Str, _T("Icon"))) return (TCHAR*) RT_GROUP_ICON; + if (!_tcsicmp(Str, _T("CursorImage"))) return (TCHAR*) RT_CURSOR; + if (!_tcsicmp(Str, _T("Cursor"))) return (TCHAR*) RT_GROUP_CURSOR; + if (!_tcsicmp(Str, _T("Dialog"))) return (TCHAR*) RT_DIALOG; + if (!_tcsicmp(Str, _T("Menu"))) return (TCHAR*) MAKEINTRESOURCE(4); + if (!_tcsicmp(Str, _T("Version"))) return (TCHAR*) RT_VERSION; + if (!_tcsicmp(Str, _T("HTML"))) return (TCHAR*) MAKEINTRESOURCE(23); + if (!_tcsicmp(Str, _T("Manifest"))) return (TCHAR*) MAKEINTRESOURCE(24); + int succ, num = ParseSimpleInt(Str, &succ); + return succ && IS_INTRESOURCE(num) ? (TCHAR*) MAKEINTRESOURCE(num) : NULL; + } + return *Str ? Str : NULL; +} + +#include "exehead/resource.h" // IDI_ICON2 +#include "exehead/config.h" // NSIS_DEFAULT_LANG +const TCHAR* CResourceEditor::ParseResourceNameString(const TCHAR*Str) { + if (Str[0] == _T('#')) + { + ++Str; + if (!_tcsicmp(Str, _T("Version"))) return (TCHAR*) MAKEINTRESOURCE(1); + if (!_tcsicmp(Str, _T("Icon"))) return (TCHAR*) MAKEINTRESOURCE(IDI_ICON2); + int succ, num = ParseSimpleInt(Str, &succ); + return succ && IS_INTRESOURCE(num) ? (TCHAR*) MAKEINTRESOURCE(num) : NULL; + } + return EditorSupportsStringNames() && *Str ? Str : NULL; +} + +LANGID CResourceEditor::ParseResourceLangString(const TCHAR*Str) { + if (!_tcsicmp(Str, _T("Any"))) return ANYLANGID; + if (!_tcsicmp(Str, _T("All"))) return ALLLANGID; + if (!_tcsicmp(Str, _T("Neutral"))) return 0x0000; //MAKELANGID(0, 0); + if (!_tcsicmp(Str, _T("Default"))) return NSIS_DEFAULT_LANG; + int succ, num = ParseSimpleInt(Str, &succ); + return succ ? num : INVALIDLANGID; +} + +LANGID CResourceEditor::ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang) { + if (!(*Type = ParseResourceTypeString(*Type))) return INVALIDLANGID; + if (!(*Name = ParseResourceNameString(*Name))) return INVALIDLANGID; + return ParseResourceLangString(Lang); +} + +template static WORD GetDependentType(T Type) +{ + if (!IS_INTRESOURCE((size_t) Type)) return 0; + if (MAKEINTRESOURCE((size_t) Type) == RT_GROUP_ICON) return (WORD)(size_t) RT_ICON; + if (MAKEINTRESOURCE((size_t) Type) == RT_GROUP_CURSOR) return (WORD)(size_t) RT_CURSOR; + return 0; +} + ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // CResourceEditor @@ -154,7 +219,8 @@ CResourceEditor::~CResourceEditor() { ////////////////////////////////////////////////////////////////////// #define FINDRESOURCE_NAME_FIRSTITEM ( (WINWCHAR*)(~(size_t)0) ) -CResourceDataEntry* CResourceEditor::FindResource(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language) { + +CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryW(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language) { int i = m_cResDir->Find(Type); if (-1 != i) { CResourceDirectory* pND = m_cResDir->GetEntry(i)->GetSubDirectory(); @@ -163,15 +229,20 @@ CResourceDataEntry* CResourceEditor::FindResource(const WINWCHAR* Type, const WI CResourceDirectory* pLD = pND->GetEntry(i)->GetSubDirectory(); i = ANYLANGID == Language ? 0 : pLD->Find(Language); if (-1 != i) - return pLD->GetEntry(i)->GetDataEntry(); + return pLD->GetEntry(i); } } return 0; } -// Adds/Replaces/Removes a resource. +CResourceDataEntry* CResourceEditor::FindResource(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language) { + CResourceDirectoryEntry*pRDE = FindResourceLanguageDirEntryW(Type, Name, Language); + return pRDE ? pRDE->GetDataEntry() : 0; +} + +// Adds/Replaces/Removes a simple resource. // If lpData is 0 UpdateResource removes the resource. -bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize) { +bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize, TYPEMANIPULATION Manip) { CResourceDirectory* nameDir = 0; CResourceDirectory* langDir = 0; CResourceDataEntry* data = 0; @@ -190,9 +261,42 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName, } } } - - if (lpData) { + + bool deleteoperation = !lpData, success = true, handlecomplexicon = false; + WORD dependenttype = GetDependentType(szType); + + if (dependenttype && Manip != TM_RAW) { + if (Manip == TM_AUTO && ((size_t) szType == (size_t) RT_GROUP_ICON || (size_t) szType == (size_t) RT_GROUP_CURSOR)) + Manip = TM_ICON; + + if (Manip & TM_ICON) + handlecomplexicon = true; + + if (handlecomplexicon && !deleteoperation) + if (Manip == TM_AUTO || (Manip & TM_ICONRSRC)) + return false; // It is impossible to add a resource icon from a plain data buffer because it doesn't use offsets for the images + + if ((size_t) szType == (size_t) RT_GROUP_ICON && (size_t) szName == (size_t) IDI_ICON2) + return false; // The main icon is special, don't allow high-level RT_GROUP_ICON updates to touch RT_ICON. + } + + if (lpData && Manip == TM_AUTO && (size_t) szType == (size_t) RT_BITMAP) + { + if (IsBMPFile(lpData, dwSize)) + { + lpData += 14, dwSize -= 14; // Remove BITMAPFILEHEADER (blogs.msdn.microsoft.com/oldnewthing/20091211-00/?p=15693#) + Manip = TM_RAW; + } + } + + if (!deleteoperation) { // Replace/Add the resource + if (handlecomplexicon) { + if (data) + DeleteIconImagesW(szType, szName, wLanguage); // Delete the RT_ICONs that belong to the RT_GROUP_ICON we are replacing + return AddExtraIconFromFile(szType, szName, wLanguage, lpData, dwSize); // Add RT_GROUP_ICON and RT_ICONs + } + if (data) { data->SetData(lpData, dwSize); return true; @@ -218,6 +322,9 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName, } } else if (data) { + if (handlecomplexicon) { + success = DeleteIconImagesW(szType, szName, wLanguage); // Delete the RT_ICONs that belong to the RT_GROUP_ICON we are deleting + } // Delete the resource delete data; langDir->RemoveEntry(iLangIdx); @@ -231,8 +338,9 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName, } } } - else return false; - return true; + else + success = false; + return success; } #ifndef _UNICODE @@ -259,18 +367,60 @@ static void FreeUnicodeResString(WINWCHAR* s) { #endif // ~_WIN32 #endif // ~_UNICODE - -bool CResourceEditor::UpdateResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize) { +CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryT(const TCHAR* Type, const TCHAR* Name, LANGID Language) { + assert(!EditorSupportsStringNames() && sizeof(Name)); #if defined(_WIN32) && defined(_UNICODE) - return UpdateResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize); + return FindResourceLanguageDirEntryW((WINWCHAR*)Type, (WINWCHAR*)Name, Language); #else - WINWCHAR* szwType = ResStringToUnicode(szType); - bool result = UpdateResourceW(szwType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize); + WINWCHAR* szwType = ResStringToUnicode(Type); + CResourceDirectoryEntry* result = FindResourceLanguageDirEntryW(szwType, (WINWCHAR*)Name, Language); FreeUnicodeResString(szwType); return result; #endif } +bool CResourceEditor::UpdateResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize, TYPEMANIPULATION Manip) { + assert(!EditorSupportsStringNames() && sizeof(szName)); +#if defined(_WIN32) && defined(_UNICODE) + return UpdateResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize, Manip); +#else + WINWCHAR* szwType = ResStringToUnicode(szType); + bool result = UpdateResourceW(szwType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize, Manip); + FreeUnicodeResString(szwType); + return result; +#endif +} + +bool CResourceEditor::UpdateResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage, FILE*Data, TYPEMANIPULATION Manip) { + assert(!EditorSupportsStringNames() && sizeof(szName)); + bool result = false; + unsigned long size; + BYTE *data = alloc_and_read_file(Data, size); + if (!data) + return false; + WORD dependenttype = GetDependentType(szType); + if (Manip == TM_AUTO) + if (dependenttype && IsICOCURFile(data, size)) + Manip = TM_ICONFILE; + result = UpdateResourceT(szType, szName, wLanguage, data, size, Manip); + free(data); + return result; +} + +bool CResourceEditor::DeleteResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage, TYPEMANIPULATION Manip) { + if (wLanguage != ALLLANGID) + return UpdateResourceT(szType, szName, wLanguage, 0, 0, Manip); + + assert(!EditorSupportsStringNames() && sizeof(szName)); + const TCHAR*name = MAKEINTRESOURCE(szName); + unsigned int deleted = 0; + for (;; ++deleted) { + CResourceDirectoryEntry*pDir = FindResourceLanguageDirEntryT(szType, name, ANYLANGID); + if (!pDir || !UpdateResourceT(szType, szName, pDir->GetId(), 0, 0, Manip)) break; + } + return deleted != 0; +} + // Returns a copy of the requested resource // Returns 0 if the requested resource can't be found BYTE* CResourceEditor::GetResourceW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage) { @@ -279,6 +429,7 @@ BYTE* CResourceEditor::GetResourceW(const WINWCHAR* szType, WINWCHAR* szName, LA } BYTE* CResourceEditor::GetResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage) { + assert(!EditorSupportsStringNames() && sizeof(szName)); #if defined(_WIN32) && defined(_UNICODE) return GetResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); #else @@ -297,6 +448,7 @@ int CResourceEditor::GetResourceSizeW(const WINWCHAR* szType, WINWCHAR* szName, } int CResourceEditor::GetResourceSizeT(const TCHAR* szType, WORD szName, LANGID wLanguage) { + assert(!EditorSupportsStringNames()); #if defined(_WIN32) && defined(_UNICODE) return GetResourceSizeW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); #else @@ -307,6 +459,14 @@ int CResourceEditor::GetResourceSizeT(const TCHAR* szType, WORD szName, LANGID w #endif } +bool CResourceEditor::ResourceExistsT(const TCHAR* szType, WORD szName, LANGID wLanguage, LANGID*pFoundLanguage) { + assert(!EditorSupportsStringNames() && sizeof(szName)); + if (wLanguage == ALLLANGID) wLanguage = ANYLANGID; + CResourceDirectoryEntry *pRDE = FindResourceLanguageDirEntryT(szType, MAKEINTRESOURCE(szName), wLanguage); + if (pFoundLanguage) *pFoundLanguage = pRDE ? pRDE->GetId() : INVALIDLANGID; + return pRDE != 0; +} + // Returns the offset of the requested resource in the original PE // Returns -1 if the requested resource can't be found DWORD CResourceEditor::GetResourceOffsetW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage) { @@ -315,6 +475,7 @@ DWORD CResourceEditor::GetResourceOffsetW(const WINWCHAR* szType, WINWCHAR* szNa } DWORD CResourceEditor::GetResourceOffsetT(const TCHAR* szType, WORD szName, LANGID wLanguage) { + assert(!EditorSupportsStringNames() && sizeof(szName)); #if defined(_WIN32) && defined(_UNICODE) return GetResourceOffsetW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); #else @@ -545,6 +706,97 @@ bool CResourceEditor::SetPESectionVirtualSize(const char* pszSectionName, DWORD return false; } +static char* GetFirstICOCURGroupEntry(const void*headerdata, WORD*pImgType, WORD*pCount) { + WORD *p16 = (WORD*) headerdata; + if (IsICOCURFile(headerdata)) { // The first 6+ bytes of the resource format matches the file format + if (pImgType) + *pImgType = FIX_ENDIAN_INT16(p16[1]) == 2 ? (WORD)(size_t) RT_CURSOR : (WORD)(size_t) RT_ICON; + if (pCount) + *pCount = FIX_ENDIAN_INT16(p16[2]); + return ((char*) headerdata) + 6; + } + return 0; +} + +bool CResourceEditor::DeleteIconImages(const CResourceDirectoryEntry& LangDir) { + WORD imgType, count, *pRGE = (WORD*) GetFirstICOCURGroupEntry(LangDir.GetDataEntry()->GetData(), &imgType, &count); + if (!pRGE) + return false; + for (unsigned int i = 0, cbRGE = 14; i < count; ++i, pRGE += cbRGE / sizeof(*pRGE)) + if (!DeleteResource(MAKEINTRESOURCE(imgType), FIX_ENDIAN_INT16(pRGE[6]), LangDir.GetId())) + return false; + return true; +} + +bool CResourceEditor::DeleteIconImagesW(const WINWCHAR* OwnerType, WINWCHAR* Name, LANGID LangId) { + CResourceDirectoryEntry*pLangDir = FindResourceLanguageDirEntryW(OwnerType, Name, LangId); + return pLangDir ? DeleteIconImages(*pLangDir) : false; +} + +static WORD FindFreeIconImageId(CResourceEditor& re, WORD ImgType) { + for (unsigned int i = MAIN_ICON_LAST_IMAGE + 1; i <= 0xffff; ++i) + if (!re.ResourceExists(MAKEINTRESOURCE(ImgType), (WORD) i, CResourceEditor::ANYLANGID)) + return (WORD) i; + return 0; +} + +bool CResourceEditor::AddExtraIconFromFile(const WINWCHAR* Type, WINWCHAR* Name, LANGID LangId, BYTE* Data, DWORD Size) { + typedef struct { BYTE Width, Height, Palette, Reserved; WORD Planes, BPP; UINT32 Size, Offset; } FILEICOGROUPENTRY; + typedef struct { BYTE Width, Height, Palette, Reserved; WORD Planes, BPP, SizeLo, SizeHi, Id; } RSRCICOGROUPENTRY; + typedef struct { WORD Width, Height; WORD Planes, BPP, SizeLo, SizeHi, Id; } RSRCCURGROUPENTRY; //msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx + assert(sizeof(RSRCICOGROUPENTRY) == 12+2 && sizeof(FILEICOGROUPENTRY) == 12+4); + assert(sizeof(RSRCICOGROUPENTRY) == sizeof(RSRCCURGROUPENTRY)); + + WORD failed = 0, count, imgType, imgId; + FILEICOGROUPENTRY *pSrcFGE = (FILEICOGROUPENTRY*) GetFirstICOCURGroupEntry(Data, &imgType, &count); + if (!pSrcFGE) + return false; + + unsigned int cbDstGrp = 6 + (count * sizeof(RSRCICOGROUPENTRY)); + BYTE *pDstGrp = new BYTE[cbDstGrp], isCursor = MAKEINTRESOURCE(imgType) == RT_CURSOR; + memcpy(pDstGrp, Data, 6); + RSRCICOGROUPENTRY *pDstRGE = (RSRCICOGROUPENTRY*) (((char*) pDstGrp) + 6); + + for (unsigned int i = 0; i < count; ++i) { + if (!(imgId = FindFreeIconImageId(*this, imgType))) + goto fail; + memcpy(&pDstRGE[i], &pSrcFGE[i], sizeof(RSRCICOGROUPENTRY)); // Copy the image information + pDstRGE[i].Id = FIX_ENDIAN_INT16(imgId); // and assign the new resource id + UINT32 imgSize = FIX_ENDIAN_INT32(pSrcFGE[i].Size); + BYTE *pImg = (BYTE*) (((char*) Data) + FIX_ENDIAN_INT32(pSrcFGE[i].Offset)), *pCursor = 0; + + if (isCursor) { // We must prepend the hotspot to the image data and change the group entry + GENERICIMAGEINFO info; + if (/*!IsPNGFile(pImg, imgSize, &info) &&*/ !GetDIBHeaderInfo(pImg, imgSize, info)) // Are PNG cursor images allowed? + goto fail; + + typedef struct { WORD x, y; } CURSORIMGHDR; //msdn.microsoft.com/en-us/library/windows/desktop/ms648017(v=vs.85).aspx + pCursor = new BYTE[imgSize + 4]; + CURSORIMGHDR *pLH = (CURSORIMGHDR*) pCursor; + pLH->x = pSrcFGE[i].Planes, pLH->y = pSrcFGE[i].BPP; + memcpy(pCursor + sizeof(CURSORIMGHDR), pImg, imgSize); + pImg = pCursor, imgSize += 4; // Our new image data is ready + + RSRCCURGROUPENTRY *pCGE = (RSRCCURGROUPENTRY*) &pDstRGE[i]; + pCGE->Width = FIX_ENDIAN_INT16(info.Width > 0xffff ? 0 : (WORD) info.Width); + pCGE->Height = FIX_ENDIAN_INT16(info.Height > 0xffff ? 0 : (WORD) info.Height); + pCGE->Planes = FIX_ENDIAN_INT16(info.Planes), pCGE->BPP = FIX_ENDIAN_INT16(info.BPP); + ((FILEICOGROUPENTRY*)pCGE)->Size = FIX_ENDIAN_INT32(imgSize); // The size of the image data has changed + } + + bool succ = UpdateResource(MAKEINTRESOURCE(imgType), imgId, LangId, pImg, imgSize); + if (pCursor) + delete [] pCursor; + if (!succ) + goto fail; + } + if (!UpdateResourceW(Type, Name, LangId, pDstGrp, cbDstGrp)) + fail: ++failed; + + delete [] pDstGrp; + return !failed; +} + ////////////////////////////////////////////////////////////////////// // Private Methods ////////////////////////////////////////////////////////////////////// @@ -699,15 +951,16 @@ void CResourceEditor::WriteRsrcSec(BYTE* pbRsrcSec) { while (!qStrings.empty()) { CResourceDirectoryEntry* cRDirE = qStrings.front(); - PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_ulWrittenAt)->UName.NameString.NameOffset = ConvertEndianness((DWORD) (seeker - pbRsrcSec)); + size_t resdirstr = (size_t)(seeker - pbRsrcSec); + assert(RALIGN(resdirstr, 4) == resdirstr); // PE spec says these are ?machine? word aligned + PMY_IMAGE_RESOURCE_DIRECTORY_ENTRY(cRDirE->m_ulWrittenAt)->UName.NameString.NameOffset = ConvertEndianness((DWORD) resdirstr); const WINWCHAR* szName = cRDirE->GetName(); - WORD iLen = (WORD) WinWStrLen(szName) + 1; - + WORD iLen = (WORD) (WinWStrLen(szName)); // No terminator *(WORD*)seeker = ConvertEndianness(iLen); - CopyMemory(seeker + sizeof(WORD), szName, iLen*sizeof(WINWCHAR)); + CopyMemory(seeker + sizeof(WORD), szName, iLen * sizeof(WINWCHAR)); - seeker += RALIGN(iLen * sizeof(WINWCHAR) + sizeof(WORD), 4); + seeker += RALIGN(sizeof(WORD) + (iLen * sizeof(WINWCHAR)), 4); qStrings.pop(); } diff --git a/Source/ResourceEditor.h b/Source/ResourceEditor.h index 3c202db3..fdc00ce0 100644 --- a/Source/ResourceEditor.h +++ b/Source/ResourceEditor.h @@ -29,6 +29,8 @@ #include #include +#define MAIN_ICON_LAST_IMAGE 99 // Main icon is special, we must reserve space for installer/uninstaller images + #ifdef _WIN32 #include #else @@ -121,15 +123,26 @@ class CResourceEditor { public: CResourceEditor(void* pbPE, int iSize, bool bKeepData = true); virtual ~CResourceEditor(); - enum { ANYLANGID = 0xffff }; + enum { ANYLANGID = 0xffff, INVALIDLANGID = 0xffff-1, ALLLANGID = 0xffff-2 }; + typedef enum { TM_RAW = 0, TM_ICONFILE = 0x01, TM_ICONRSRC = 0x02, TM_ICON = (TM_ICONFILE|TM_ICONRSRC), TM_AUTO = 0x04 } TYPEMANIPULATION; // On POSIX+Unicode GetResource(RT_VERSION,..) is not TCHAR nor WINWCHAR, it is WCHAR/UINT16 (MAKEINTRESOURCEW). // If it passes IS_INTRESOURCE we must allow it. // Use TCHAR* for real strings. If you need to pass in a WINWCHAR*, make GetResourceW public... - template bool UpdateResource(const T*Type, WORD Name, LANGID Lang, BYTE*Data, DWORD Size) + template bool UpdateResource(const T*Type, WORD Name, LANGID Lang, BYTE*Data, DWORD Size, TYPEMANIPULATION Manip = TM_RAW) { if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return false; } - return UpdateResourceT((const TCHAR*) Type, Name, Lang, Data, Size); + return UpdateResourceT((const TCHAR*) Type, Name, Lang, Data, Size, Manip); + } + template bool UpdateResource(const T*Type, WORD Name, LANGID Lang, FILE*Data, TYPEMANIPULATION Manip = TM_AUTO) + { + if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return false; } + return UpdateResourceT((const TCHAR*) Type, Name, Lang, Data, Manip); + } + template bool DeleteResource(const T*Type, WORD Name, LANGID Lang, TYPEMANIPULATION Manip = TM_RAW) + { + if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return false; } + return DeleteResourceT((const TCHAR*) Type, Name, Lang, Manip); } template BYTE* GetResource(const T*Type, WORD Name, LANGID Lang) { @@ -146,16 +159,24 @@ public: if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return -1; } return GetResourceOffsetT((const TCHAR*) Type, Name, Lang); } + template bool ResourceExists(const T*Type, WORD Name, LANGID Lang, LANGID*pFoundLanguage = 0) + { + if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return false; } + return ResourceExistsT((const TCHAR*) Type, Name, Lang, pFoundLanguage); + } template BYTE* GetFirstResource(const T*Type, size_t&cbData) { if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return NULL; } return GetFirstResourceT((const TCHAR*) Type, cbData); } - bool UpdateResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize); + bool UpdateResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize, TYPEMANIPULATION Manip = TM_RAW); + bool UpdateResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage, FILE*Data, TYPEMANIPULATION Manip = TM_AUTO); + bool DeleteResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage, TYPEMANIPULATION Manip = TM_RAW); BYTE* GetResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage); int GetResourceSizeT (const TCHAR* szType, WORD szName, LANGID wLanguage); DWORD GetResourceOffsetT(const TCHAR* szType, WORD szName, LANGID wLanguage); + bool ResourceExistsT (const TCHAR* szType, WORD szName, LANGID wLanguage, LANGID*pFoundLanguage = 0); BYTE* GetFirstResourceT (const TCHAR* szType, size_t&cbData); void FreeResource(BYTE* pbResource); @@ -174,13 +195,24 @@ public: DWORD *pdwSectionIndex = NULL ); + static const TCHAR* ParseResourceTypeString(const TCHAR*String); + static const TCHAR* ParseResourceNameString(const TCHAR*String); + static LANGID ParseResourceLangString(const TCHAR*String); + static LANGID ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang); + static bool EditorSupportsStringNames() { return false; } // UpdateResource/GetResource do not support string names (yet) + private: - bool UpdateResourceW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize); + bool UpdateResourceW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize, TYPEMANIPULATION Manip = TM_RAW); BYTE* GetResourceW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage); int GetResourceSizeW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage); DWORD GetResourceOffsetW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage); BYTE* GetFirstResourceW (const WINWCHAR* szType, size_t&cbData); CResourceDataEntry* FindResource(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language); + CResourceDirectoryEntry* FindResourceLanguageDirEntryW(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language); + CResourceDirectoryEntry* FindResourceLanguageDirEntryT(const TCHAR* Type, const TCHAR* Name, LANGID Language); + bool DeleteIconImages(const CResourceDirectoryEntry& LangDir); + bool DeleteIconImagesW(const WINWCHAR* OwnerType, WINWCHAR* Name, LANGID LangId); + bool AddExtraIconFromFile(const WINWCHAR* Type, WINWCHAR* Name, LANGID LangId, BYTE* Data, DWORD Size); BYTE* DupData(CResourceDataEntry*pDE); // Free with FreeResource CResourceDirectory* ScanDirectory(PRESOURCE_DIRECTORY rdRoot, PRESOURCE_DIRECTORY rdToScan); diff --git a/Source/Tests/ResourceEditor.cpp b/Source/Tests/ResourceEditor.cpp index 8bf57398..e708c3f7 100644 --- a/Source/Tests/ResourceEditor.cpp +++ b/Source/Tests/ResourceEditor.cpp @@ -3,16 +3,19 @@ #include +extern unsigned char original_pe[8704]; + class CResourceEditorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( CResourceEditorTest ); CPPUNIT_TEST( testCorrectness ); + CPPUNIT_TEST( testBMP ); CPPUNIT_TEST_SUITE_END(); public: + enum { defnameid = 1337, deflangid = 1033 }; + void testCorrectness() { - extern unsigned char original_pe[8704]; - CResourceEditor re(original_pe, sizeof(original_pe)); DWORD size; @@ -32,6 +35,31 @@ public: delete [] saved_pe; } + void testBMP() { + CResourceEditor re(original_pe, sizeof(original_pe)); + static const BYTE file_12_4bpp [] = { // BMP with the old header + 66,77,134,0,0,0,0,0,0,0,74,0,0,0,12,0,0,0,19,0,5,0,1,0,4,0,222,24,0,74,74,74,0,255,0,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,2,34,2,0,2,32,0,32,0,32,0,0,2,32,2,34,32,34,2,34, + 34,0,0,0,2,2,2,32,2,34,2,34,0,32,0,0,0,34,2,2,34,34,2,32,34,32,0,0,2,34,2,32,0,32,0,34,0,0,0,0 + }; + bool succ; + int ressize, bmpfilehdrsize = 14; + BYTE *resdata; + + CPPUNIT_ASSERT( (succ = re.UpdateResource(RT_BITMAP, defnameid, deflangid, (BYTE*) file_12_4bpp, sizeof(file_12_4bpp), CResourceEditor::TM_AUTO)) ); + if (succ) + { + ressize = re.GetResourceSize(RT_BITMAP, defnameid, deflangid); + CPPUNIT_ASSERT( ressize == sizeof(file_12_4bpp) - bmpfilehdrsize ); + if ((resdata = re.GetResource(RT_BITMAP, defnameid, deflangid))) + { + CPPUNIT_ASSERT_EQUAL( 0, memcmp(resdata, file_12_4bpp + bmpfilehdrsize, ressize) ); + re.FreeResource(resdata); + } + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION( CResourceEditorTest ); diff --git a/Source/Tests/SConscript b/Source/Tests/SConscript index b197a2f3..a3211b86 100644 --- a/Source/Tests/SConscript +++ b/Source/Tests/SConscript @@ -17,6 +17,7 @@ required = Split(""" dirreader.cpp growbuf.cpp mmap.cpp + BinInterop.cpp ResourceEditor.cpp util.cpp winchar.cpp @@ -72,6 +73,7 @@ extralibs = Split(""" iconv pthread user32 + oleaut32 """) scripts = Split(""" diff --git a/Source/build.cpp b/Source/build.cpp index d5061216..d381a954 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -2389,6 +2389,10 @@ int CEXEBuild::SetManifest() if (manifest == "") return PS_OK; + // TODO: Ideally we should allow this but we must be sure that the manifest is custom and not a manifest from the stub + //if (res_editor->ResourceExists(MAKEINTRESOURCE(24), 1, CResourceEditor::ANYLANGID)) + // return PS_OK; // Allow user to completely override the manifest with PEAddResource + // Saved directly as binary into the exe. res_editor->UpdateResource(MAKEINTRESOURCE(24), 1, NSIS_DEFAULT_LANG, (LPBYTE) manifest.c_str(), (DWORD)manifest.length()); } @@ -2616,7 +2620,7 @@ int CEXEBuild::write_output(void) } // Set icon - set_icon(res_editor, IDI_ICON2, installer_icon, uninstaller_icon); + set_main_icon(res_editor, IDI_ICON2, installer_icon, uninstaller_icon); // Save all changes to the exe header close_res_editor(); diff --git a/Source/icon.cpp b/Source/icon.cpp index 696ed206..ca967a6d 100644 --- a/Source/icon.cpp +++ b/Source/icon.cpp @@ -31,6 +31,17 @@ extern int g_display_errors; extern FILE *g_output; #define SIZEOF_RSRC_ICON_GROUP_ENTRY 14 +enum { ICO_TYPE_ICON = 1, ICO_TYPE_CURSOR = 2 }; + +static bool is_valid_header(const void*headerdata) +{ + assert(sizeof(IconGroupHeader) == 6); + IconGroupHeader*pGH = (IconGroupHeader*) headerdata; + if (pGH->wReserved != FIX_ENDIAN_INT16(0x0000)) return false; + WORD type = FIX_ENDIAN_INT16(pGH->wType); + if (type != ICO_TYPE_ICON && type != ICO_TYPE_CURSOR) return false; + return FIX_ENDIAN_INT16(pGH->wCount) != 0; +} static FILE * open_icon(const TCHAR* filename, IconGroupHeader& igh) { @@ -44,16 +55,15 @@ static FILE * open_icon(const TCHAR* filename, IconGroupHeader& igh) throw runtime_error("unable to read header from file"); } - FIX_ENDIAN_INT16_INPLACE(igh.wIsIcon); - FIX_ENDIAN_INT16_INPLACE(igh.wReserved); - FIX_ENDIAN_INT16_INPLACE(igh.wCount); - - if (igh.wIsIcon != 1 || igh.wReserved != 0) + if (!is_valid_header(&igh)) { fclose(f); throw runtime_error("invalid icon file"); } + FIX_ENDIAN_INT16_INPLACE(igh.wReserved); + FIX_ENDIAN_INT16_INPLACE(igh.wType); + FIX_ENDIAN_INT16_INPLACE(igh.wCount); return f; } @@ -270,7 +280,7 @@ static LPBYTE generate_icon_group(IconGroup icon, IconPairs order, bool first) IconGroupHeader* header = (IconGroupHeader*) group; header->wReserved = 0; - header->wIsIcon = FIX_ENDIAN_INT16(1); + header->wType = FIX_ENDIAN_INT16(ICO_TYPE_ICON); header->wCount = FIX_ENDIAN_INT16((WORD)icon.size()); order = sort_pairs(order, first); @@ -289,7 +299,7 @@ static LPBYTE generate_icon_group(IconGroup icon, IconPairs order, bool first) } // set_icon, must get an initialized resource editor -void set_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon2) +void set_main_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon2) { IconPairs order = get_icon_order(icon1, icon2); @@ -300,12 +310,12 @@ void set_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon size_t group_size = sizeof(IconGroupHeader) // header + order.size() * SIZEOF_RSRC_ICON_GROUP_ENTRY; // entries - re->UpdateResource(RT_GROUP_ICON, wIconId, NSIS_DEFAULT_LANG, group1, (DWORD)group_size); + re->UpdateResource(RT_GROUP_ICON, wIconId, NSIS_DEFAULT_LANG, group1, (DWORD)group_size, CResourceEditor::TM_RAW); // Update the group entries but not the images destroy_icon_group(group1); // delete old icons unsigned i = 1; - while (re->UpdateResource(RT_ICON, i++, NSIS_DEFAULT_LANG, 0, 0)); + while (i <= MAIN_ICON_LAST_IMAGE && re->UpdateResource(RT_ICON, i++, NSIS_DEFAULT_LANG, 0, 0)); // set new icons IconGroup::size_type order_index; @@ -391,7 +401,8 @@ unsigned char* generate_uninstall_icon_data(IconGroup icon1, IconGroup icon2, si // Fill the array of icons for uninstall with their offsets // Returns zero on failure -int generate_unicons_offsets(LPBYTE exeHeader, size_t exeHeaderSize, LPBYTE uninstIconData, WORD wIconId) { +int generate_unicons_offsets(LPBYTE exeHeader, size_t exeHeaderSize, LPBYTE uninstIconData, WORD wIconId) +{ try { DWORD offset; diff --git a/Source/icon.h b/Source/icon.h index d705bd45..a56d6ef8 100644 --- a/Source/icon.h +++ b/Source/icon.h @@ -26,7 +26,7 @@ typedef struct { WORD wReserved; - WORD wIsIcon; + WORD wType; WORD wCount; } IconGroupHeader; @@ -66,7 +66,7 @@ IconGroup load_icon_file(const TCHAR* filename); IconGroup load_icon_res(CResourceEditor* re, WORD id); void free_loaded_icon(IconGroup icon); -void set_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon2); +void set_main_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon2); #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT // returns the data of the uninstaller icon (inside filename) that should replace the installer icon data diff --git a/Source/script.cpp b/Source/script.cpp index fa92c197..a4c5a8f8 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -2240,6 +2240,65 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) return PS_ERROR; #endif //~ NSIS_CONFIG_VISIBLE_SUPPORT + case TOK_PEADDRESOURCE: + { + init_res_editor(); + int tokidx = 1, ovr = 0, rep = 0; + if (!_tcsicmp(line.gettoken_str(tokidx), _T("/OVERWRITE"))) // Update the resource even if it exists + ++ovr, ++tokidx; + else if (!_tcsicmp(line.gettoken_str(tokidx), _T("/REPLACE"))) // Only update existing resource + ++rep, ++tokidx; + const TCHAR *rt = line.gettoken_str(tokidx+1), *rnraw = line.gettoken_str(tokidx+2), *rlraw = line.gettoken_str(tokidx+3); + if (!*rlraw) + rlraw = _T("Default"); // Language parameter is optional + LANGID rl = res_editor->ParseResourceTypeNameLangString(&rt, &rnraw, rlraw), foundrl; + if (rl == CResourceEditor::INVALIDLANGID || rl == CResourceEditor::ALLLANGID) + PRINTHELP(); + WORD rn = (WORD)(size_t) rnraw; assert(!CResourceEditor::EditorSupportsStringNames()); + bool exists = res_editor->ResourceExists(rt, rn, rl, &foundrl); + if (rl == CResourceEditor::ANYLANGID) + rl = exists ? foundrl : NSIS_DEFAULT_LANG; + if ((rep && !exists) || (!ovr && exists)) + { + ERROR_MSG(_T("Error: Resource %") NPRIns _T("\n"), rep ? ("does not exist") : ("already exists")); + return PS_ERROR; + } + int result = PS_ERROR; + if (FILE*f = FOPEN(line.gettoken_str(tokidx+0), ("rb"))) + { + if (res_editor->UpdateResource(rt, rn, rl, f, CResourceEditor::TM_AUTO)) + { + SCRIPT_MSG(_T("PEAddResource: %") NPRIs _T("=%") NPRIs _T("\n"), make_friendly_resource_path(rt, rnraw, rl).c_str(), line.gettoken_str(tokidx+0)); + result = PS_OK; + } + fclose(f); + } + return result; + } + return PS_OK; + case TOK_PEREMOVERESOURCE: + { + init_res_editor(); + int tokidx = 1, noerr = 0; + if (!_tcsicmp(line.gettoken_str(tokidx), _T("/NOERRORS"))) + ++noerr, ++tokidx; + const TCHAR *rt = line.gettoken_str(tokidx+0), *rnraw = line.gettoken_str(tokidx+1), *rlraw = line.gettoken_str(tokidx+2); + LANGID rl = res_editor->ParseResourceTypeNameLangString(&rt, &rnraw, rlraw); + if (rl == CResourceEditor::INVALIDLANGID || rl == CResourceEditor::ANYLANGID) + PRINTHELP(); + WORD rn = (WORD)(size_t) rnraw; assert(!CResourceEditor::EditorSupportsStringNames()); + if (!noerr && !res_editor->ResourceExists(rt, rn, rl)) + { + ERROR_MSG(_T("Error: Resource %") NPRIns _T("\n"), ("does not exist")); + return PS_ERROR; + } + if (res_editor->DeleteResource(rt, rn, rl, CResourceEditor::TM_AUTO)) + SCRIPT_MSG(_T("PERemoveResource: %") NPRIs _T("\n"), make_friendly_resource_path(rt, rnraw, rl).c_str()); + else if (!noerr) + return PS_ERROR; + } + return PS_OK; + case TOK_PEDLLCHARACTERISTICS: { int s1, s2; @@ -2249,7 +2308,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) SCRIPT_MSG(_T("PEDllCharacteristics: 0x%.4x -> 0x%.4x\n"), org, PEDllCharacteristics); } return PS_OK; - + case TOK_PESUBSYSVER: { unsigned int mj, mi; diff --git a/Source/tokens.cpp b/Source/tokens.cpp index 78e03330..12f18584 100644 --- a/Source/tokens.cpp +++ b/Source/tokens.cpp @@ -193,7 +193,7 @@ static tokenType tokenlist[TOK__LAST] = {TOK_SETAUTOCLOSE,_T("SetAutoClose"),1,0,_T("(false|true)"),TP_CODE}, {TOK_SETCTLCOLORS,_T("SetCtlColors"),2,2,_T("hwnd [/BRANDING] [text_color] [transparent|bg_color]"),TP_CODE}, {TOK_SETBRANDINGIMAGE,_T("SetBrandingImage"),1,2,_T("[/IMGID=image_item_id_in_dialog] [/RESIZETOFIT] bitmap.bmp"),TP_CODE}, -{TOK_LOADANDSETIMAGE,_T("LoadAndSetImage"),4,3,_T("[/EXERESOURCE] [/STRINGID] [/RESIZETOFIT[WIDTH|HEIGHT]] ctrl imagetype lrflags image"),TP_CODE}, +{TOK_LOADANDSETIMAGE,_T("LoadAndSetImage"),4,4,_T("[/EXERESOURCE] [/STRINGID] [/RESIZETOFIT[WIDTH|HEIGHT]] ctrl imagetype lrflags image"),TP_CODE}, {TOK_SETCOMPRESS,_T("SetCompress"),1,0,_T("(off|auto|force)"),TP_ALL}, {TOK_SETCOMPRESSOR,_T("SetCompressor"),1,2,_T("[/FINAL] [/SOLID] (zlib|bzip2|lzma)"),TP_GLOBAL}, {TOK_SETCOMPRESSORDICTSIZE,_T("SetCompressorDictSize"),1,0,_T("dict_size_mb"),TP_ALL}, @@ -250,6 +250,8 @@ static tokenType tokenlist[TOK__LAST] = {TOK_WRITEREGEXPANDSTR,_T("WriteRegExpandStr"),4,0,_T("rootkey subkey entry_name new_value_string\n root_key=(HKCR[32|64]|HKLM[32|64]|HKCU[32|64]|HKU|HKCC|HKDD|HKPD|SHCTX)"),TP_CODE}, {TOK_WRITEREGNONE,_T("WriteRegNone"),3,1,_T("rootkey subkey entry_name [hex_data]"),TP_CODE}, {TOK_WRITEUNINSTALLER,_T("WriteUninstaller"),1,0,_T("uninstall_exe_name"),TP_CODE}, +{TOK_PEADDRESOURCE,_T("PEAddResource"),3,2,_T("[/OVERWRITE|/REPLACE] file restype resname [reslang]"),TP_GLOBAL}, +{TOK_PEREMOVERESOURCE,_T("PERemoveResource"),3,1,_T("[/NOERRORS] restype resname reslang|ALL"),TP_GLOBAL}, {TOK_PEDLLCHARACTERISTICS,_T("PEDllCharacteristics"),2,0,_T("addbits removebits"),TP_GLOBAL}, {TOK_PESUBSYSVER,_T("PESubsysVer"),1,0,_T("major.minor"),TP_GLOBAL}, {TOK_XPSTYLE,_T("XPStyle"),1,0,_T("(on|off)"),TP_GLOBAL}, diff --git a/Source/tokens.h b/Source/tokens.h index 1a392ee6..81c54d8d 100644 --- a/Source/tokens.h +++ b/Source/tokens.h @@ -59,6 +59,8 @@ enum TOK_BRANDINGTEXT, TOK_FILEERRORTEXT, TOK_INSTPROGRESSFLAGS, + TOK_PEADDRESOURCE, + TOK_PEREMOVERESOURCE, TOK_PEDLLCHARACTERISTICS, TOK_PESUBSYSVER, TOK_XPSTYLE, diff --git a/Source/util.cpp b/Source/util.cpp index 0316ca47..59524ca6 100644 --- a/Source/util.cpp +++ b/Source/util.cpp @@ -25,6 +25,7 @@ #include "strlist.h" #include "winchar.h" #include "utf.h" +#include "BinInterop.h" #ifndef _WIN32 # include @@ -142,64 +143,37 @@ size_t my_strftime(TCHAR *s, size_t max, const TCHAR *fmt, const struct tm *tm) int update_bitmap(CResourceEditor* re, WORD id, const TCHAR* filename, int width/*=0*/, int height/*=0*/, int maxbpp/*=0*/) { FILE *f = FOPEN(filename, ("rb")); if (!f) return -1; - if (fgetc(f) != 'B' || fgetc(f) != 'M') { - fclose(f); - return -2; + signed char hdr[14+124], retval = -2; + size_t size = fread(hdr, 1, sizeof(hdr), f); + GENERICIMAGEINFO info; + if (IsBMPFile(hdr, size, &info) && 0 == fseek(f, 0, SEEK_SET)) + { + if ((width && width != (int) info.Width) || (height && height != (int) info.Height)) + retval = -3; + else if (maxbpp && maxbpp < info.BPP) + retval = -4; + else if (re->UpdateResource(RT_BITMAP, id, NSIS_DEFAULT_LANG, f, CResourceEditor::TM_AUTO)) + retval = 0; } - if (width != 0) { - INT32 biWidth; - fseek(f, 18, SEEK_SET); // Seek to the width member of the header - size_t nio = fread(&biWidth, sizeof(INT32), 1, f); - FIX_ENDIAN_INT32_INPLACE(biWidth); - if (nio != 1 || width != biWidth) { - fclose(f); - return -3; - } - } - if (height != 0) { - INT32 biHeight; - fseek(f, 22, SEEK_SET); // Seek to the height member of the header - size_t nio = fread(&biHeight, sizeof(INT32), 1, f); - FIX_ENDIAN_INT32_INPLACE(biHeight); - // Bitmap height can be negative too... - if (nio != 1 || height != abs(biHeight)) { - fclose(f); - return -3; - } - } - if (maxbpp != 0) { - WORD biBitCount; - fseek(f, 28, SEEK_SET); // Seek to the bitcount member of the header - size_t nio = fread(&biBitCount, sizeof(WORD), 1, f); - FIX_ENDIAN_INT16_INPLACE(biBitCount); - if (nio != 1 || biBitCount > maxbpp) { - fclose(f); - return -4; - } - } - DWORD dwSize; - fseek(f, 2, SEEK_SET); - size_t nio = fread(&dwSize, sizeof(DWORD), 1, f); - if (nio != 1) { - fclose(f); - return -3; - } - FIX_ENDIAN_INT32_INPLACE(dwSize); - dwSize -= 14; - unsigned char* bitmap = (unsigned char*)malloc(dwSize); - if (!bitmap) { - fclose(f); - throw bad_alloc(); - } - bool gotbmdata = !fseek(f, 14, SEEK_SET) && dwSize == fread(bitmap, 1, dwSize, f); - int retval = gotbmdata ? 0 : -2; fclose(f); - if (gotbmdata) - re->UpdateResource(RT_BITMAP, id, NSIS_DEFAULT_LANG, bitmap, dwSize); - free(bitmap); return retval; } +tstring make_friendly_resource_path(const TCHAR*rt, const TCHAR*rn, LANGID rl) +{ + tstring s = _T(""); + TCHAR buf[42], sep = _T('\\'); + s += IS_INTRESOURCE(rt) ? (wsprintf(buf, _T("#%d"), (int)(size_t) rt), buf) : rt; + s += sep; + s += IS_INTRESOURCE(rn) ? (wsprintf(buf, _T("#%d"), (int)(size_t) rn), buf) : rn; + s += sep; + if (rl == CResourceEditor::ALLLANGID) + s += _T("All"); + else + s += (wsprintf(buf, _T("%d"), (int)(size_t) rl), buf); + return s; +} + #ifndef _WIN32 void PathConvertWinToPosix(char*p); diff --git a/Source/util.h b/Source/util.h index abf6c6f9..c4694212 100644 --- a/Source/util.h +++ b/Source/util.h @@ -40,6 +40,7 @@ size_t my_strftime(TCHAR *s, size_t max, const TCHAR *fmt, const struct tm *tm) // Adds the bitmap in filename using resource editor re as id id. // If width or height are specified it will also make sure the bitmap is in that size int update_bitmap(CResourceEditor* re, WORD id, const TCHAR* filename, int width=0, int height=0, int maxbpp=0); +tstring make_friendly_resource_path(const TCHAR*rt, const TCHAR*rn, LANGID rl); TCHAR* create_tempfile_path(); tstring get_full_path(const tstring& path);