Icon now supports the res:// protocol

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7328 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2021-09-18 20:24:41 +00:00
parent cc431158fd
commit 167e53df8a
6 changed files with 266 additions and 127 deletions

View file

@ -30,6 +30,7 @@ using namespace std;
// Utilities
//////////////////////////////////////////////////////////////////////
#define FIRSTRESDIRSTRADDRESS ( (WINWCHAR*)(~(size_t)0) )
#define WCHARPTR2WINWCHARPTR(s) ( (WINWCHAR*) (s) ) // Only for WinSDK structs like IMAGE_RESOURCE_DIR_STRING_U where we cannot change the WCHAR type!
#define RALIGN(dwToAlign, dwAlignOn) ((dwToAlign%dwAlignOn == 0) ? dwToAlign : dwToAlign - (dwToAlign%dwAlignOn) + dwAlignOn)
#define ALIGN(dwToAlign, dwAlignOn) dwToAlign = RALIGN((dwToAlign), (dwAlignOn))
@ -37,6 +38,31 @@ using namespace std;
static inline DWORD ConvertEndianness(DWORD d) { return FIX_ENDIAN_INT32(d); }
static inline WORD ConvertEndianness(WORD w) { return FIX_ENDIAN_INT16(w); }
#if !(defined(_WIN32) && defined(_UNICODE))
static void FreeUnicodeResString(WINWCHAR* s) {
if (!IS_INTRESOURCE(s) && FIRSTRESDIRSTRADDRESS != (WINWCHAR*) s)
free(s);
}
static WINWCHAR* ResStringToUnicode(const char *s) {
if (IS_INTRESOURCE(s)) return MAKEINTRESOURCEWINW((ULONG_PTR)s);
if (FIRSTRESDIRSTRADDRESS == (WINWCHAR*) s) return (WINWCHAR*) s;
WINWCHAR *ws = WinWStrDupFromTChar(s);
if (!ws) throw std::runtime_error("Unicode conversion failed");
return ws;
}
#endif //~ !(_WIN32 && _UNICODE)
struct UTF16LEResString {
WINWCHAR *m_s;
operator WINWCHAR*() const { return m_s; }
#if defined(_WIN32) && defined(_UNICODE)
UTF16LEResString(const TCHAR*tstr) : m_s((WINWCHAR*) tstr) {}
#else
UTF16LEResString(const TCHAR*tstr) : m_s(ResStringToUnicode(tstr)) { }
~UTF16LEResString() { FreeUnicodeResString(m_s); }
#endif
};
PIMAGE_NT_HEADERS CResourceEditor::GetNTHeaders(BYTE* pbPE) {
// Get dos header
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) pbPE;
@ -140,12 +166,13 @@ const TCHAR* CResourceEditor::ParseResourceTypeString(const TCHAR*Str)
#include "exehead/resource.h" // IDI_ICON2
#include "exehead/config.h" // NSIS_DEFAULT_LANG
const TCHAR* CResourceEditor::ParseResourceNameString(const TCHAR*Str) {
const TCHAR* CResourceEditor::ParseResourceNameString(const TCHAR*Str, bool AllowFirst) {
if (Str[0] == _T('#'))
{
++Str;
if (!_tcsicmp(Str, _T("Version"))) return (TCHAR*) MAKEINTRESOURCE(1);
if (!_tcsicmp(Str, _T("Icon"))) return (TCHAR*) MAKEINTRESOURCE(IDI_ICON2);
if (AllowFirst && Str[0] == '?' && !Str[1]) return (TCHAR*) FIRSTRESDIRSTRADDRESS;
int succ, num = ParseSimpleInt(Str, &succ);
return succ && IS_INTRESOURCE(num) ? (TCHAR*) MAKEINTRESOURCE(num) : NULL;
}
@ -161,19 +188,26 @@ LANGID CResourceEditor::ParseResourceLangString(const TCHAR*Str) {
return succ ? num : INVALIDLANGID;
}
LANGID CResourceEditor::ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang) {
LANGID CResourceEditor::ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang, bool AllowFirst) {
if (!(*Type = ParseResourceTypeString(*Type))) return INVALIDLANGID;
if (!(*Name = ParseResourceNameString(*Name))) return INVALIDLANGID;
if (!(*Name = ParseResourceNameString(*Name, AllowFirst))) return INVALIDLANGID;
return ParseResourceLangString(Lang);
}
UINT CResourceEditor::IsResProtocol(const TCHAR*Url)
{
if ('r' == S7ChLwr(Url[0]) && 'e' == S7ChLwr(Url[1]) && 's' == S7ChLwr(Url[2]))
if (':' == Url[3] && '/' == Url[4] && '/' == Url[5])
return 6;
return 0;
}
static TCHAR* ParseResProtocolAlloc(const TCHAR*Url, const TCHAR*&Type, const TCHAR*&Name, LANGID&Lang) {
//msdn.microsoft.com/library/aa767740#res Protocol
TCHAR proto[42], *path = 0, *buf = 0, *pD, ch;
UINT prefix = 6, mswin = Platform_IsWindows(), bad = false, pipe = 0, skip = 0;
my_strncpy(proto, Url, prefix+!0);
TCHAR *path = 0, *buf = 0, *pD, ch;
UINT prefix, mswin = Platform_IsWindows(), bad = false, pipe = 0, skip = 0;
size_t typestart = 0, namestart = 0, i, cch;
if (lowercase(tstring(proto)).compare(_T("res://")) != 0)
if (!(prefix = CResourceEditor::IsResProtocol(Url)))
return path;
for (Url += prefix, i = 0; Url[i]; ++i)
if (Url[i] == '/')
@ -201,7 +235,7 @@ static TCHAR* ParseResProtocolAlloc(const TCHAR*Url, const TCHAR*&Type, const TC
}
if (!(*pD = (mswin && ch == '/') ? '\\' : ch)) break; // Convert path if needed and stop at the end.
}
Lang = CResourceEditor::ParseResourceTypeNameLangString(&rt, &rn, rl);
Lang = CResourceEditor::ParseResourceTypeNameLangString(&rt, &rn, rl, true);
if (!bad && Lang != CResourceEditor::INVALIDLANGID)
path = buf, Type = rt, Name = rn;
}
@ -275,26 +309,44 @@ CResourceEditor::~CResourceEditor() {
// Methods
//////////////////////////////////////////////////////////////////////
#define FINDRESOURCE_NAME_FIRSTITEM ( (WINWCHAR*)(~(size_t)0) )
bool CResourceEditor::CanOpen(const void*Data, size_t Size)
{
return 'P' == GetExeType(Data, Size); // We only understand PE, not NE/LE
}
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();
i = FINDRESOURCE_NAME_FIRSTITEM == Name ? 0 : pND->Find(Name);
if (-1 != i) {
CResourceDirectory* pLD = pND->GetEntry(i)->GetSubDirectory();
i = ANYLANGID == Language ? 0 : pLD->Find(Language);
if (-1 != i)
return pLD->GetEntry(i);
CResourceDataEntry* CResourceEditor::FindResourceW(const WINWCHAR*RT, const WINWCHAR*RN, LANGID RL, CResourceDirectoryEntry**ppTE, CResourceDirectoryEntry**ppNE, CResourceDirectoryEntry**ppLE) const {
int i = m_cResDir->Find(RT);
CResourceDirectoryEntry*pTDE = -1 != i ? m_cResDir->GetEntry(i) : 0, *pNDE, *pLDE;
if (pTDE) {
CResourceDirectory *pND = pTDE->GetSubDirectory();
i = FIRSTRESDIRSTRADDRESS == RN ? 0 : pND->Find(RN);
if ((pNDE = (-1 != i) ? pND->GetEntry(i) : 0)) {
CResourceDirectory *pLD = pNDE->GetSubDirectory();
i = ANYLANGID == RL ? 0 : pLD->Find(RL);
if ((pLDE = (-1 != i) ? pLD->GetEntry(i) : 0)) {
if (ppTE) *ppTE = pTDE;
if (ppNE) *ppNE = pNDE;
if (ppLE) *ppLE = pLDE;
return pLDE->GetDataEntry();
}
}
}
return 0;
}
CResourceDataEntry* CResourceEditor::FindResource(const WINWCHAR* Type, const WINWCHAR* Name, LANGID Language) {
CResourceDirectoryEntry*pRDE = FindResourceLanguageDirEntryW(Type, Name, Language);
return pRDE ? pRDE->GetDataEntry() : 0;
CResourceDataEntry* CResourceEditor::FindResourceT(const TCHAR*RT, const TCHAR*RN, LANGID RL, CResourceDirectoryEntry**ppTE, CResourceDirectoryEntry**ppNE, CResourceDirectoryEntry**ppLE) const {
return FindResourceW(UTF16LEResString(RT), UTF16LEResString(RN), RL, ppTE, ppNE, ppLE);
}
CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryW(const WINWCHAR* RT, const WINWCHAR* RN, LANGID RL) const {
CResourceDirectoryEntry*pLDE = 0;
FindResourceW(RT, RN, RL, 0, 0, &pLDE);
return pLDE;
}
CResourceDataEntry* CResourceEditor::FindResource(const WINWCHAR* RT, const WINWCHAR* RN, LANGID RL) const {
CResourceDirectoryEntry*pDE = FindResourceLanguageDirEntryW(RT, RN, RL);
return pDE ? pDE->GetDataEntry() : 0;
}
template<class T> static void UpdateManipulationType(CResourceEditor::TYPEMANIPULATION &Manip, const T* szType, const void*Data, size_t Size) {
@ -408,37 +460,13 @@ bool CResourceEditor::UpdateResourceW(const WINWCHAR* szType, WINWCHAR* szName,
return success;
}
#ifndef _UNICODE
static WINWCHAR* ResStringToUnicode(const char *szString) {
if (IS_INTRESOURCE(szString)) return MAKEINTRESOURCEWINW((ULONG_PTR)szString);
WINWCHAR *s = WinWStrDupFromTChar(szString);
if (!s) throw std::bad_alloc();
return s;
}
static void FreeUnicodeResString(WINWCHAR* szwString) {
if (!IS_INTRESOURCE(szwString)) free(szwString);
}
#else
#ifndef _WIN32
static WINWCHAR* ResStringToUnicode(const TCHAR *s) {
if (IS_INTRESOURCE(s)) return MAKEINTRESOURCEWINW((ULONG_PTR)s);
WCToUTF16LEHlpr cnv;
if (!cnv.Create(s)) throw std::runtime_error("Unicode conversion failed");
return (WINWCHAR*) cnv.Detach();
}
static void FreeUnicodeResString(WINWCHAR* s) {
if (!IS_INTRESOURCE(s)) free(s);
}
#endif // ~_WIN32
#endif // ~_UNICODE
CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryT(const TCHAR* Type, const TCHAR* Name, LANGID Language) {
assert(!EditorSupportsStringNames() && sizeof(Name));
CResourceDirectoryEntry* CResourceEditor::FindResourceLanguageDirEntryT(const TCHAR* RT, const TCHAR* RN, LANGID RL) const {
assert(!EditorSupportsStringNames() && sizeof(RN));
#if defined(_WIN32) && defined(_UNICODE)
return FindResourceLanguageDirEntryW((WINWCHAR*)Type, (WINWCHAR*)Name, Language);
return FindResourceLanguageDirEntryW((WINWCHAR*)RT, (WINWCHAR*)RN, RL);
#else
WINWCHAR* szwType = ResStringToUnicode(Type);
CResourceDirectoryEntry* result = FindResourceLanguageDirEntryW(szwType, (WINWCHAR*)Name, Language);
WINWCHAR* szwType = ResStringToUnicode(RT);
CResourceDirectoryEntry* result = FindResourceLanguageDirEntryW(szwType, (WINWCHAR*)RN, RL);
FreeUnicodeResString(szwType);
return result;
#endif
@ -550,7 +578,7 @@ DWORD CResourceEditor::GetResourceOffsetT(const TCHAR* szType, WORD szName, LANG
// Returns a copy of the resource data from the first resource of a specific type
BYTE* CResourceEditor::GetFirstResourceW(const WINWCHAR* szType, size_t&cbData) {
CResourceDataEntry *pDE = FindResource(szType, FINDRESOURCE_NAME_FIRSTITEM, ANYLANGID);
CResourceDataEntry *pDE = FindResource(szType, FIRSTRESDIRSTRADDRESS, ANYLANGID);
if (pDE)
{
cbData = pDE->GetSize();
@ -863,17 +891,28 @@ bool CResourceEditor::AddExtraIconFromFile(const WINWCHAR* Type, WINWCHAR* Name,
return !failed;
}
bool CResourceEditor::UpdateResourceFromExternalT(const TCHAR* Type, WORD Name, LANGID Lang, const TCHAR*File, TYPEMANIPULATION Manip)
{
bool success = false;
const TCHAR *srctype, *srcname;
LANGID srclang = 0;
TCHAR *resproto = ParseResProtocolAlloc(File, srctype, srcname, srclang);
template<class C, class P> static bool Contains(C&Map, P*p) {
return p && (SIZE_T) Map.base <= (SIZE_T) p && (SIZE_T) Map.base + Map.size > (SIZE_T) p;
}
void CResourceEditor::FreeExternal(EXTERNAL&X) {
assert(sizeof(FILEVIEW) <= sizeof(X.Map));
if (X.Data) {
FILEVIEW &map = *(FILEVIEW*) &X.Map;
if (!Contains(map, X.Data)) free(X.Data);
close_file_view(map);
free(X.FreeThis); // ParseResProtocolAlloc
}
}
// Maps a file into memory and locates a resource inside it, a manipulated from a resource inside it or the file itself. Free with FreeExternal.
const TCHAR* CResourceEditor::MapExternal(const TCHAR*File, TYPEMANIPULATION Manip, EXTERNAL&X) {
TCHAR *resproto = ParseResProtocolAlloc(File, X.RT, X.RN, X.RL);
if (resproto) {
File = resproto;
}
FILEVIEW map;
FILEVIEW &map = *(FILEVIEW*) &X.Map;
size_t datasize;
char *filedata = create_file_view_readonly(File, map), *data = 0, *dataalloc = 0;
if (filedata) {
@ -881,15 +920,17 @@ bool CResourceEditor::UpdateResourceFromExternalT(const TCHAR* Type, WORD Name,
signed char exetype = GetExeType(filedata, map.size);
if (exetype == 'P') {
CResourceEditor re(filedata, (int) map.size);
WORD ordsrcname = (WORD)(size_t) srcname;
if (!re.EditorSupportsStringNames() && !IS_INTRESOURCE(srcname)) {
assert(!"Unsupported name");
srclang = INVALIDLANGID;
DWORD ofs, siz = 0, firstname = X.RN == (TCHAR*) FIRSTRESDIRSTRADDRESS;
CResourceDirectoryEntry*pNRDE, *pLRDE;
CResourceDataEntry*pRE = re.FindResourceT(X.RT, X.RN, X.RL, 0, &pNRDE, &pLRDE);
if (pRE) {
const WINWCHAR *wrn = pNRDE->GetNameOrId();
if (firstname) X.RN = IS_INTRESOURCE(wrn) ? MAKEINTRESOURCE((size_t) wrn) : _T("");
X.RL = pLRDE->GetId();
ofs = pRE->GetOffset(), siz = pRE->GetSize();
}
DWORD ofs = re.GetResourceOffset(srctype, ordsrcname, srclang);
DWORD siz = re.GetResourceSize(srctype, ordsrcname, srclang);
if (IsIcoCurGroupType(srctype) && (Manip == TM_AUTO || (Manip & TM_ICON))) { // Must create a fake .ico file
data = dataalloc = (char*) re.ExtractIcoCur(srctype, ordsrcname, srclang, datasize), siz = 0;
if (siz && IsIcoCurGroupType(X.RT) && (Manip == TM_AUTO || (Manip & TM_ICON))) { // Must create a fake .ico file
data = dataalloc = (char*) re.ExtractIcoCur(*pRE, X.RL, datasize), siz = 0;
}
if (siz && siz != DWORD(-1)) {
data = filedata + ofs, datasize = siz; // Raw resource data
@ -899,26 +940,43 @@ bool CResourceEditor::UpdateResourceFromExternalT(const TCHAR* Type, WORD Name,
else {
data = filedata, datasize = map.size; // Just a normal file
}
if (data && (DWORD) datasize == datasize) {
success = this->UpdateResource(Type, Name, Lang, (BYTE*) data, (DWORD) datasize, Manip);
}
close_file_view(map);
}
free(dataalloc);
free(resproto);
X.Data = (BYTE*) data, X.cbData = datasize;
X.FreeThis = resproto;
if (data) return File;
FreeExternal(X);
return 0;
}
bool CResourceEditor::UpdateResourceFromExternalT(const TCHAR* Type, WORD Name, LANGID Lang, const TCHAR*File, TYPEMANIPULATION Manip) {
EXTERNAL x;
size_t &datasize = x.cbData;
bool success = false;
const TCHAR *parsedpath = MapExternal(File, Manip, x);
if (parsedpath) {
if ((DWORD) datasize == datasize) {
success = this->UpdateResource(Type, Name, Lang, (BYTE*) x.Data, (DWORD) datasize, Manip);
}
FreeExternal(x);
}
return success;
}
CResourceDataEntry* CResourceEditor::FindIcoCurDataEntry(WORD Type, WORD Id, LANGID PrefLang) {
CResourceDataEntry* CResourceEditor::FindIcoCurDataEntry(WORD Type, WORD Id, LANGID PrefLang) const {
CResourceDataEntry*pRDE = FindResource(MAKEINTRESOURCEWINW(Type), MAKEINTRESOURCEWINW(Id), PrefLang);
return pRDE ? pRDE : FindResource(MAKEINTRESOURCEWINW(Type), MAKEINTRESOURCEWINW(Id), ANYLANGID);
}
BYTE* CResourceEditor::ExtractIcoCurW(const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, size_t&cbData) {
BYTE* CResourceEditor::ExtractIcoCurW(const WINWCHAR* szType, const WINWCHAR* szName, LANGID wLanguage, size_t&cbData) const {
CResourceDirectoryEntry*pLangDir = FindResourceLanguageDirEntryW(szType, szName, wLanguage);
if (!pLangDir)
return 0;
CResourceDataEntry*pRDE = pLangDir->GetDataEntry();
return ExtractIcoCur(*pRDE, pLangDir->GetId(), cbData); // Uses the "real" LANGID
}
BYTE* CResourceEditor::ExtractIcoCur(const CResourceDataEntry&rde, LANGID ChildLang, size_t&cbData) const {
const CResourceDataEntry*pRDE = &rde;
BYTE*pSH = pRDE->GetData(), cbRGE = 14, cbFGE = 16, *pResData;
DWORD i, cbRes, failed = false;
if (pRDE->GetSize() < 6) // Must at least have a ICO file header
@ -932,7 +990,7 @@ BYTE* CResourceEditor::ExtractIcoCurW(const WINWCHAR* szType, WINWCHAR* szName,
// Get the size of all images
for (i = 0, pRGE = pFirstRGE; i < count; ++i, pRGE += cbRGE / sizeof(*pRGE)) {
pRDE = FindIcoCurDataEntry(imgResType, ((RSRCICOGROUPENTRY*)pRGE)->Id, wLanguage);
pRDE = FindIcoCurDataEntry(imgResType, ((RSRCICOGROUPENTRY*)pRGE)->Id, ChildLang);
if (pRDE && pRDE->GetData()) cbImages += FIX_ENDIAN_INT32(((FILEICOGROUPENTRY*)pRGE)->Size); else count = 0;
}
// Build the .ICO file
@ -940,7 +998,7 @@ BYTE* CResourceEditor::ExtractIcoCurW(const WINWCHAR* szType, WINWCHAR* szName,
if (count && (pDH = (WORD*) malloc(cbTot += cbImages))) {
pDH[0] = 0x0000, pDH[1] = FIX_ENDIAN_INT16(isCursor ? 2 : 1), pDH[2] = FIX_ENDIAN_INT16(count);
for (i = 0, pRGE = pFirstRGE; i < count; ++i, pRGE += cbRGE / sizeof(*pRGE)) {
pRDE = FindIcoCurDataEntry(imgResType, ((RSRCICOGROUPENTRY*)pRGE)->Id, wLanguage);
pRDE = FindIcoCurDataEntry(imgResType, ((RSRCICOGROUPENTRY*)pRGE)->Id, ChildLang);
pResData = pRDE->GetData(), cbRes = pRDE->GetSize();
FILEICOGROUPENTRY*pFGE = (FILEICOGROUPENTRY*) ((char*)pDH + grpsOfs);
memcpy(pFGE, pRGE, cbRGE), pFGE->Offset = FIX_ENDIAN_INT32(imgsOfs); // Initialize ICO group entry
@ -972,7 +1030,7 @@ BYTE* CResourceEditor::ExtractIcoCurW(const WINWCHAR* szType, WINWCHAR* szName,
return (BYTE*) pDH;
}
BYTE* CResourceEditor::ExtractIcoCurT(const TCHAR* szType, WORD szName, LANGID wLanguage, size_t&cbData) {
BYTE* CResourceEditor::ExtractIcoCurT(const TCHAR* szType, WORD szName, LANGID wLanguage, size_t&cbData) const {
assert(!EditorSupportsStringNames() && sizeof(szName));
#if defined(_WIN32) && defined(_UNICODE)
return ExtractIcoCurW((WINWCHAR*)szType, MAKEINTRESOURCEWINW(szName), wLanguage, cbData);
@ -1454,7 +1512,7 @@ CResourceDataEntry::~CResourceDataEntry() {
// To save memory this function doesn't give you a copy of the data
// Don't mess with the data returned from this function!
BYTE* CResourceDataEntry::GetData() {
BYTE* CResourceDataEntry::GetData() const {
return m_pbData;
}
@ -1471,14 +1529,14 @@ void CResourceDataEntry::SetData(BYTE* pbData, DWORD dwSize, DWORD dwCodePage) {
m_dwOffset = DWORD(-1); // unset
}
DWORD CResourceDataEntry::GetSize() {
DWORD CResourceDataEntry::GetSize() const {
return m_dwSize;
}
DWORD CResourceDataEntry::GetCodePage() {
DWORD CResourceDataEntry::GetCodePage() const {
return m_dwCodePage;
}
DWORD CResourceDataEntry::GetOffset() {
DWORD CResourceDataEntry::GetOffset() const {
return m_dwOffset;
}