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:
parent
797e745de5
commit
978ac79a5d
17 changed files with 561 additions and 103 deletions
|
@ -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|\\<b\\>admin\\</b\\>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
#include "util.h"
|
||||
#include "winchar.h"
|
||||
#include <queue>
|
||||
#include <cassert>
|
||||
#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<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
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
#define MAIN_ICON_LAST_IMAGE 99 // Main icon is special, we must reserve space for installer/uninstaller images
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winnt.h>
|
||||
#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<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; }
|
||||
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)
|
||||
{
|
||||
|
@ -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<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)
|
||||
{
|
||||
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);
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
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 );
|
||||
|
|
|
@ -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("""
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -59,6 +59,8 @@ enum
|
|||
TOK_BRANDINGTEXT,
|
||||
TOK_FILEERRORTEXT,
|
||||
TOK_INSTPROGRESSFLAGS,
|
||||
TOK_PEADDRESOURCE,
|
||||
TOK_PEREMOVERESOURCE,
|
||||
TOK_PEDLLCHARACTERISTICS,
|
||||
TOK_PESUBSYSVER,
|
||||
TOK_XPSTYLE,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "strlist.h"
|
||||
#include "winchar.h"
|
||||
#include "utf.h"
|
||||
#include "BinInterop.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# 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*/) {
|
||||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue