Added PEAddResource and PERemoveResource

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7079 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2019-02-10 20:45:40 +00:00
parent 797e745de5
commit 978ac79a5d
17 changed files with 561 additions and 103 deletions

View file

@ -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. 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 \S2{arequestexecutionlevel} RequestExecutionLevel
\c none|user|highest|\\<b\\>admin\\</b\\> \c none|user|highest|\\<b\\>admin\\</b\\>

View file

@ -8,6 +8,8 @@ Released on ? ?th, 201?
\S2{} Minor Changes \S2{} Minor Changes
\b Added \R{apeaddresource}{PEAddResource} and \R{aperemoveresource}{PERemoveResource}
\b Added \R{loadandsetimage}{LoadAndSetImage} \b Added \R{loadandsetimage}{LoadAndSetImage}
\S2{} Translations \S2{} Translations

View file

@ -26,6 +26,7 @@
#define LE2HE16 FIX_ENDIAN_INT16 // Little-endian 2 Host-endian #define LE2HE16 FIX_ENDIAN_INT16 // Little-endian 2 Host-endian
#define LE2HE32 FIX_ENDIAN_INT32 #define LE2HE32 FIX_ENDIAN_INT32
#define HE2LE16 LE2HE16 #define HE2LE16 LE2HE16
#define HE2BE32 BE2HE32
const size_t invalid_res_id = ~(size_t)0; const size_t invalid_res_id = ~(size_t)0;
FILE* MSTLB_fopen(const TCHAR*filepath, size_t*pResId) 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); if (!result) result = GetDLLVersionFromVXD(filepath, high, low);
return result; 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;
}

View file

@ -27,4 +27,25 @@ bool GetTLBVersion(const TCHAR *filepath, DWORD &high, DWORD &low);
bool GetDLLVersion(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 #endif //~ NSIS_BININTEROP_H

View file

@ -149,12 +149,14 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG;
# define FIX_ENDIAN_INT32(x) (x) # define FIX_ENDIAN_INT32(x) (x)
# define FIX_ENDIAN_INT16_INPLACE(x) ((void)(x)) # define FIX_ENDIAN_INT16_INPLACE(x) ((void)(x))
# define FIX_ENDIAN_INT16(x) (x) # define FIX_ENDIAN_INT16(x) (x)
# define BE2HE32(x) SWAP_ENDIAN_INT32(x)
#else #else
# define FIX_ENDIAN_INT64(x) SWAP_ENDIAN_INT64(x) # define FIX_ENDIAN_INT64(x) SWAP_ENDIAN_INT64(x)
# define FIX_ENDIAN_INT32_INPLACE(x) ((x) = SWAP_ENDIAN_INT32(x)) # define FIX_ENDIAN_INT32_INPLACE(x) ((x) = SWAP_ENDIAN_INT32(x))
# define FIX_ENDIAN_INT32(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_INPLACE(x) ((x) = SWAP_ENDIAN_INT16(x))
# define FIX_ENDIAN_INT16(x) SWAP_ENDIAN_INT16(x) # define FIX_ENDIAN_INT16(x) SWAP_ENDIAN_INT16(x)
# define BE2HE32(x) (x)
#endif #endif
#define SWAP_ENDIAN_INT64(x) ( \ #define SWAP_ENDIAN_INT64(x) ( \
(((x)&0xFF00000000000000) >> 56) | \ (((x)&0xFF00000000000000) >> 56) | \
@ -230,7 +232,11 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG;
# define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i)))) # define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
# endif # endif
# ifndef MAKEINTRESOURCE # ifndef MAKEINTRESOURCE
# define MAKEINTRESOURCE MAKEINTRESOURCEA # ifdef UNICODE
# define MAKEINTRESOURCE MAKEINTRESOURCEW
# else
# define MAKEINTRESOURCE MAKEINTRESOURCEA
# endif
# endif # endif
# ifndef IMAGE_FIRST_SECTION # ifndef IMAGE_FIRST_SECTION
# define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (ULONG_PTR) h + \ # define IMAGE_FIRST_SECTION(h) ( PIMAGE_SECTION_HEADER( (ULONG_PTR) h + \
@ -520,6 +526,10 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG;
// resources // resources
#ifndef RT_CURSOR
# define RT_CURSOR MAKEINTRESOURCE(1)
# define RT_GROUP_CURSOR MAKEINTRESOURCE(1 + 11)
#endif
#ifndef RT_BITMAP #ifndef RT_BITMAP
# define RT_BITMAP MAKEINTRESOURCE(2) # define RT_BITMAP MAKEINTRESOURCE(2)
#endif #endif
@ -530,7 +540,7 @@ typedef DWORDLONG ULONGLONG,*PULONGLONG;
# define RT_DIALOG MAKEINTRESOURCE(5) # define RT_DIALOG MAKEINTRESOURCE(5)
#endif #endif
#ifndef RT_GROUP_ICON #ifndef RT_GROUP_ICON
# define RT_GROUP_ICON MAKEINTRESOURCE(14) # define RT_GROUP_ICON MAKEINTRESOURCE(3 + 11)
#endif #endif
#ifndef RT_VERSION #ifndef RT_VERSION
# define RT_VERSION MAKEINTRESOURCE(16) # define RT_VERSION MAKEINTRESOURCE(16)

View file

@ -20,8 +20,10 @@
#include "util.h" #include "util.h"
#include "winchar.h" #include "winchar.h"
#include <queue> #include <queue>
#include <cassert>
#include "tchar.h" #include "tchar.h"
#include "utf.h" #include "utf.h"
#include "BinInterop.h"
using namespace std; 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 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 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) { static inline DWORD ConvertEndianness(DWORD d) { return FIX_ENDIAN_INT32(d); }
return FIX_ENDIAN_INT32(d); static inline WORD ConvertEndianness(WORD w) { return FIX_ENDIAN_INT16(w); }
}
static inline WORD ConvertEndianness(WORD w) {
return FIX_ENDIAN_INT16(w);
}
PIMAGE_NT_HEADERS CResourceEditor::GetNTHeaders(BYTE* pbPE) { PIMAGE_NT_HEADERS CResourceEditor::GetNTHeaders(BYTE* pbPE) {
// Get dos header // Get dos header
@ -110,6 +107,74 @@ PRESOURCE_DIRECTORY CResourceEditor::GetResourceDirectory(
return PRESOURCE_DIRECTORY(pbPE + dwResSecPtr); 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<class T> 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 // CResourceEditor
@ -154,7 +219,8 @@ CResourceEditor::~CResourceEditor() {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
#define FINDRESOURCE_NAME_FIRSTITEM ( (WINWCHAR*)(~(size_t)0) ) #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); int i = m_cResDir->Find(Type);
if (-1 != i) { if (-1 != i) {
CResourceDirectory* pND = m_cResDir->GetEntry(i)->GetSubDirectory(); 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(); CResourceDirectory* pLD = pND->GetEntry(i)->GetSubDirectory();
i = ANYLANGID == Language ? 0 : pLD->Find(Language); i = ANYLANGID == Language ? 0 : pLD->Find(Language);
if (-1 != i) if (-1 != i)
return pLD->GetEntry(i)->GetDataEntry(); return pLD->GetEntry(i);
} }
} }
return 0; 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. // 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* nameDir = 0;
CResourceDirectory* langDir = 0; CResourceDirectory* langDir = 0;
CResourceDataEntry* data = 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 // 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) { if (data) {
data->SetData(lpData, dwSize); data->SetData(lpData, dwSize);
return true; return true;
@ -218,6 +322,9 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName,
} }
} }
else if (data) { 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 the resource
delete data; delete data;
langDir->RemoveEntry(iLangIdx); langDir->RemoveEntry(iLangIdx);
@ -231,8 +338,9 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName,
} }
} }
} }
else return false; else
return true; success = false;
return success;
} }
#ifndef _UNICODE #ifndef _UNICODE
@ -259,18 +367,60 @@ static void FreeUnicodeResString(WINWCHAR* s) {
#endif // ~_WIN32 #endif // ~_WIN32
#endif // ~_UNICODE #endif // ~_UNICODE
CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryT(const TCHAR* Type, const TCHAR* Name, LANGID Language) {
bool CResourceEditor::UpdateResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage, BYTE* lpData, DWORD dwSize) { assert(!EditorSupportsStringNames() && sizeof(Name));
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
return UpdateResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize); return FindResourceLanguageDirEntryW((WINWCHAR*)Type, (WINWCHAR*)Name, Language);
#else #else
WINWCHAR* szwType = ResStringToUnicode(szType); WINWCHAR* szwType = ResStringToUnicode(Type);
bool result = UpdateResourceW(szwType, MAKEINTRESOURCEWINW(szName), wLanguage, lpData, dwSize); CResourceDirectoryEntry* result = FindResourceLanguageDirEntryW(szwType, (WINWCHAR*)Name, Language);
FreeUnicodeResString(szwType); FreeUnicodeResString(szwType);
return result; return result;
#endif #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 a copy of the requested resource
// Returns 0 if the requested resource can't be found // Returns 0 if the requested resource can't be found
BYTE* CResourceEditor::GetResourceW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage) { 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) { BYTE* CResourceEditor::GetResourceT(const TCHAR* szType, WORD szName, LANGID wLanguage) {
assert(!EditorSupportsStringNames() && sizeof(szName));
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
return GetResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); return GetResourceW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage);
#else #else
@ -297,6 +448,7 @@ int CResourceEditor::GetResourceSizeW(const WINWCHAR* szType, WINWCHAR* szName,
} }
int CResourceEditor::GetResourceSizeT(const TCHAR* szType, WORD szName, LANGID wLanguage) { int CResourceEditor::GetResourceSizeT(const TCHAR* szType, WORD szName, LANGID wLanguage) {
assert(!EditorSupportsStringNames());
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
return GetResourceSizeW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); return GetResourceSizeW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage);
#else #else
@ -307,6 +459,14 @@ int CResourceEditor::GetResourceSizeT(const TCHAR* szType, WORD szName, LANGID w
#endif #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 the offset of the requested resource in the original PE
// Returns -1 if the requested resource can't be found // Returns -1 if the requested resource can't be found
DWORD CResourceEditor::GetResourceOffsetW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage) { 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) { DWORD CResourceEditor::GetResourceOffsetT(const TCHAR* szType, WORD szName, LANGID wLanguage) {
assert(!EditorSupportsStringNames() && sizeof(szName));
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
return GetResourceOffsetW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage); return GetResourceOffsetW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage);
#else #else
@ -545,6 +706,97 @@ bool CResourceEditor::SetPESectionVirtualSize(const char* pszSectionName, DWORD
return false; 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 // Private Methods
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -699,15 +951,16 @@ void CResourceEditor::WriteRsrcSec(BYTE* pbRsrcSec) {
while (!qStrings.empty()) { while (!qStrings.empty()) {
CResourceDirectoryEntry* cRDirE = qStrings.front(); 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(); const WINWCHAR* szName = cRDirE->GetName();
WORD iLen = (WORD) WinWStrLen(szName) + 1; WORD iLen = (WORD) (WinWStrLen(szName)); // No terminator
*(WORD*)seeker = ConvertEndianness(iLen); *(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(); qStrings.pop();
} }

View file

@ -29,6 +29,8 @@
#include <vector> #include <vector>
#include <cassert> #include <cassert>
#define MAIN_ICON_LAST_IMAGE 99 // Main icon is special, we must reserve space for installer/uninstaller images
#ifdef _WIN32 #ifdef _WIN32
#include <winnt.h> #include <winnt.h>
#else #else
@ -121,15 +123,26 @@ class CResourceEditor {
public: public:
CResourceEditor(void* pbPE, int iSize, bool bKeepData = true); CResourceEditor(void* pbPE, int iSize, bool bKeepData = true);
virtual ~CResourceEditor(); 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). // 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. // 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... // Use TCHAR* for real strings. If you need to pass in a WINWCHAR*, make GetResourceW public...
template<class T> bool UpdateResource(const T*Type, WORD Name, LANGID Lang, BYTE*Data, DWORD Size) template<class T> 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; } 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<class T> 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<class T> 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<class T> BYTE* GetResource(const T*Type, WORD Name, LANGID Lang) template<class T> 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; } if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return -1; }
return GetResourceOffsetT((const TCHAR*) Type, Name, Lang); return GetResourceOffsetT((const TCHAR*) Type, Name, Lang);
} }
template<class T> 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<class T> BYTE* GetFirstResource(const T*Type, size_t&cbData) template<class T> BYTE* GetFirstResource(const T*Type, size_t&cbData)
{ {
if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return NULL; } if (sizeof(T) != sizeof(TCHAR) && !IS_INTRESOURCE(Type)) { assert(IS_INTRESOURCE(Type)); return NULL; }
return GetFirstResourceT((const TCHAR*) Type, cbData); 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); BYTE* GetResourceT (const TCHAR* szType, WORD szName, LANGID wLanguage);
int GetResourceSizeT (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); 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); BYTE* GetFirstResourceT (const TCHAR* szType, size_t&cbData);
void FreeResource(BYTE* pbResource); void FreeResource(BYTE* pbResource);
@ -174,13 +195,24 @@ public:
DWORD *pdwSectionIndex = NULL 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: 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); BYTE* GetResourceW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage);
int GetResourceSizeW (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); DWORD GetResourceOffsetW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage);
BYTE* GetFirstResourceW (const WINWCHAR* szType, size_t&cbData); BYTE* GetFirstResourceW (const WINWCHAR* szType, size_t&cbData);
CResourceDataEntry* FindResource(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language); 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 BYTE* DupData(CResourceDataEntry*pDE); // Free with FreeResource
CResourceDirectory* ScanDirectory(PRESOURCE_DIRECTORY rdRoot, PRESOURCE_DIRECTORY rdToScan); CResourceDirectory* ScanDirectory(PRESOURCE_DIRECTORY rdRoot, PRESOURCE_DIRECTORY rdToScan);

View file

@ -3,16 +3,19 @@
#include <stdlib.h> #include <stdlib.h>
extern unsigned char original_pe[8704];
class CResourceEditorTest : public CppUnit::TestFixture { class CResourceEditorTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE( CResourceEditorTest ); CPPUNIT_TEST_SUITE( CResourceEditorTest );
CPPUNIT_TEST( testCorrectness ); CPPUNIT_TEST( testCorrectness );
CPPUNIT_TEST( testBMP );
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
public: public:
enum { defnameid = 1337, deflangid = 1033 };
void testCorrectness() { void testCorrectness() {
extern unsigned char original_pe[8704];
CResourceEditor re(original_pe, sizeof(original_pe)); CResourceEditor re(original_pe, sizeof(original_pe));
DWORD size; DWORD size;
@ -32,6 +35,31 @@ public:
delete [] saved_pe; 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 ); CPPUNIT_TEST_SUITE_REGISTRATION( CResourceEditorTest );

View file

@ -17,6 +17,7 @@ required = Split("""
dirreader.cpp dirreader.cpp
growbuf.cpp growbuf.cpp
mmap.cpp mmap.cpp
BinInterop.cpp
ResourceEditor.cpp ResourceEditor.cpp
util.cpp util.cpp
winchar.cpp winchar.cpp
@ -72,6 +73,7 @@ extralibs = Split("""
iconv iconv
pthread pthread
user32 user32
oleaut32
""") """)
scripts = Split(""" scripts = Split("""

View file

@ -2389,6 +2389,10 @@ int CEXEBuild::SetManifest()
if (manifest == "") if (manifest == "")
return PS_OK; 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. // Saved directly as binary into the exe.
res_editor->UpdateResource(MAKEINTRESOURCE(24), 1, NSIS_DEFAULT_LANG, (LPBYTE) manifest.c_str(), (DWORD)manifest.length()); 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
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 // Save all changes to the exe header
close_res_editor(); close_res_editor();

View file

@ -31,6 +31,17 @@ extern int g_display_errors;
extern FILE *g_output; extern FILE *g_output;
#define SIZEOF_RSRC_ICON_GROUP_ENTRY 14 #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) 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"); throw runtime_error("unable to read header from file");
} }
FIX_ENDIAN_INT16_INPLACE(igh.wIsIcon); if (!is_valid_header(&igh))
FIX_ENDIAN_INT16_INPLACE(igh.wReserved);
FIX_ENDIAN_INT16_INPLACE(igh.wCount);
if (igh.wIsIcon != 1 || igh.wReserved != 0)
{ {
fclose(f); fclose(f);
throw runtime_error("invalid icon file"); 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; return f;
} }
@ -270,7 +280,7 @@ static LPBYTE generate_icon_group(IconGroup icon, IconPairs order, bool first)
IconGroupHeader* header = (IconGroupHeader*) group; IconGroupHeader* header = (IconGroupHeader*) group;
header->wReserved = 0; 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()); header->wCount = FIX_ENDIAN_INT16((WORD)icon.size());
order = sort_pairs(order, first); 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 // 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); 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 size_t group_size = sizeof(IconGroupHeader) // header
+ order.size() * SIZEOF_RSRC_ICON_GROUP_ENTRY; // entries + 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); destroy_icon_group(group1);
// delete old icons // delete old icons
unsigned i = 1; 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 // set new icons
IconGroup::size_type order_index; 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 // Fill the array of icons for uninstall with their offsets
// Returns zero on failure // 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 try
{ {
DWORD offset; DWORD offset;

View file

@ -26,7 +26,7 @@
typedef struct typedef struct
{ {
WORD wReserved; WORD wReserved;
WORD wIsIcon; WORD wType;
WORD wCount; WORD wCount;
} IconGroupHeader; } IconGroupHeader;
@ -66,7 +66,7 @@ IconGroup load_icon_file(const TCHAR* filename);
IconGroup load_icon_res(CResourceEditor* re, WORD id); IconGroup load_icon_res(CResourceEditor* re, WORD id);
void free_loaded_icon(IconGroup icon); 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 #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
// returns the data of the uninstaller icon (inside filename) that should replace the installer icon data // returns the data of the uninstaller icon (inside filename) that should replace the installer icon data

View file

@ -2240,6 +2240,65 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
return PS_ERROR; return PS_ERROR;
#endif //~ NSIS_CONFIG_VISIBLE_SUPPORT #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: case TOK_PEDLLCHARACTERISTICS:
{ {
int s1, s2; 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); SCRIPT_MSG(_T("PEDllCharacteristics: 0x%.4x -> 0x%.4x\n"), org, PEDllCharacteristics);
} }
return PS_OK; return PS_OK;
case TOK_PESUBSYSVER: case TOK_PESUBSYSVER:
{ {
unsigned int mj, mi; unsigned int mj, mi;

View file

@ -193,7 +193,7 @@ static tokenType tokenlist[TOK__LAST] =
{TOK_SETAUTOCLOSE,_T("SetAutoClose"),1,0,_T("(false|true)"),TP_CODE}, {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_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_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_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_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}, {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_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_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_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_PEDLLCHARACTERISTICS,_T("PEDllCharacteristics"),2,0,_T("addbits removebits"),TP_GLOBAL},
{TOK_PESUBSYSVER,_T("PESubsysVer"),1,0,_T("major.minor"),TP_GLOBAL}, {TOK_PESUBSYSVER,_T("PESubsysVer"),1,0,_T("major.minor"),TP_GLOBAL},
{TOK_XPSTYLE,_T("XPStyle"),1,0,_T("(on|off)"),TP_GLOBAL}, {TOK_XPSTYLE,_T("XPStyle"),1,0,_T("(on|off)"),TP_GLOBAL},

View file

@ -59,6 +59,8 @@ enum
TOK_BRANDINGTEXT, TOK_BRANDINGTEXT,
TOK_FILEERRORTEXT, TOK_FILEERRORTEXT,
TOK_INSTPROGRESSFLAGS, TOK_INSTPROGRESSFLAGS,
TOK_PEADDRESOURCE,
TOK_PEREMOVERESOURCE,
TOK_PEDLLCHARACTERISTICS, TOK_PEDLLCHARACTERISTICS,
TOK_PESUBSYSVER, TOK_PESUBSYSVER,
TOK_XPSTYLE, TOK_XPSTYLE,

View file

@ -25,6 +25,7 @@
#include "strlist.h" #include "strlist.h"
#include "winchar.h" #include "winchar.h"
#include "utf.h" #include "utf.h"
#include "BinInterop.h"
#ifndef _WIN32 #ifndef _WIN32
# include <ctype.h> # include <ctype.h>
@ -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*/) { int update_bitmap(CResourceEditor* re, WORD id, const TCHAR* filename, int width/*=0*/, int height/*=0*/, int maxbpp/*=0*/) {
FILE *f = FOPEN(filename, ("rb")); FILE *f = FOPEN(filename, ("rb"));
if (!f) return -1; if (!f) return -1;
if (fgetc(f) != 'B' || fgetc(f) != 'M') { signed char hdr[14+124], retval = -2;
fclose(f); size_t size = fread(hdr, 1, sizeof(hdr), f);
return -2; 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); fclose(f);
if (gotbmdata)
re->UpdateResource(RT_BITMAP, id, NSIS_DEFAULT_LANG, bitmap, dwSize);
free(bitmap);
return retval; 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 #ifndef _WIN32
void PathConvertWinToPosix(char*p); void PathConvertWinToPosix(char*p);

View file

@ -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. // 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 // 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); 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(); TCHAR* create_tempfile_path();
tstring get_full_path(const tstring& path); tstring get_full_path(const tstring& path);