diff --git a/Source/ResourceEditor.cpp b/Source/ResourceEditor.cpp index ac47f52c..b5c54466 100644 --- a/Source/ResourceEditor.cpp +++ b/Source/ResourceEditor.cpp @@ -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 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 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; } diff --git a/Source/ResourceEditor.h b/Source/ResourceEditor.h index fdea39e7..0ed96355 100644 --- a/Source/ResourceEditor.h +++ b/Source/ResourceEditor.h @@ -180,6 +180,7 @@ public: return ExtractIcoCurT((const TCHAR*) Type, Name, Lang, cbData); } + CResourceDataEntry* FindResourceT(const TCHAR*RT, const TCHAR*RN, LANGID RL, CResourceDirectoryEntry**pTE, CResourceDirectoryEntry**pNE, CResourceDirectoryEntry**pLE) const; bool UpdateResourceFromExternalT(const TCHAR* Type, WORD Name, LANGID Lang, const TCHAR*File, TYPEMANIPULATION Manip = TM_AUTO); 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); @@ -189,7 +190,8 @@ public: 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* ExtractIcoCurT (const TCHAR* szType, WORD szName, LANGID wLanguage, size_t&cbData); + BYTE* ExtractIcoCurT (const TCHAR* szType, WORD szName, LANGID wLanguage, size_t&cbData) const; + BYTE* ExtractIcoCur(const CResourceDataEntry&rde, LANGID ChildLang, size_t&cbData) const; void FreeResource(BYTE* pbResource); // The section name must be in ASCII. @@ -208,9 +210,14 @@ public: ); static const TCHAR* ParseResourceTypeString(const TCHAR*String); - static const TCHAR* ParseResourceNameString(const TCHAR*String); + static const TCHAR* ParseResourceNameString(const TCHAR*String, bool AllowFirst = false); static LANGID ParseResourceLangString(const TCHAR*String); - static LANGID ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang); + static LANGID ParseResourceTypeNameLangString(const TCHAR**Type, const TCHAR**Name, const TCHAR*Lang, bool AllowFirst = false); + static bool CanOpen(const void*Data, size_t Size); + static UINT IsResProtocol(const TCHAR*Url); + typedef struct { BYTE*Data; size_t cbData, Map[2]; void*FreeThis; const TCHAR*RT, *RN; LANGID RL; } EXTERNAL; + static void FreeExternal(EXTERNAL&External); + static const TCHAR* MapExternal(const TCHAR*File, TYPEMANIPULATION Manip, EXTERNAL&External); static bool EditorSupportsStringNames() { return false; } // UpdateResource/GetResource do not support string names (yet) static bool EditorSupportsCursorPng() { return false; } @@ -220,14 +227,15 @@ private: 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); - BYTE* ExtractIcoCurW (const WINWCHAR* szType, WINWCHAR* szName, LANGID wLanguage, 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); + BYTE* ExtractIcoCurW (const WINWCHAR* szType, const WINWCHAR* szName, LANGID wLanguage, size_t&cbData) const; + CResourceDataEntry* FindResource(const WINWCHAR*RT, const WINWCHAR*RN, LANGID RL) const; + CResourceDataEntry* FindResourceW(const WINWCHAR*RT, const WINWCHAR*RN, LANGID RL, CResourceDirectoryEntry**ppTE, CResourceDirectoryEntry**ppNE, CResourceDirectoryEntry**ppLE) const; + CResourceDirectoryEntry* FindResourceLanguageDirEntryW(const WINWCHAR* RT, const WINWCHAR* RN, LANGID RL) const; + CResourceDirectoryEntry* FindResourceLanguageDirEntryT(const TCHAR* RT, const TCHAR* RN, LANGID RL) const; 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); - CResourceDataEntry* FindIcoCurDataEntry(WORD Type, WORD Id, LANGID PrefLang); + CResourceDataEntry* FindIcoCurDataEntry(WORD Type, WORD Id, LANGID PrefLang) const; BYTE* DupData(CResourceDataEntry*pDE); // Free with FreeResource CResourceDirectory* ScanDirectory(PRESOURCE_DIRECTORY rdRoot, PRESOURCE_DIRECTORY rdToScan); @@ -279,6 +287,7 @@ public: CResourceDirectoryEntry(const WINWCHAR* szName, CResourceDataEntry* rdeData); virtual ~CResourceDirectoryEntry(); + const WINWCHAR* GetNameOrId() const { return HasName() ? GetName() : (WINWCHAR*)(size_t) GetId(); } bool HasName() const; const WINWCHAR* GetName() const; int GetNameLength() const; @@ -306,13 +315,13 @@ public: CResourceDataEntry(BYTE* pbData, DWORD dwSize, DWORD dwCodePage = 0, DWORD dwOffset = DWORD(-1)); ~CResourceDataEntry(); - BYTE* GetData(); + BYTE* GetData() const; void SetData(BYTE* pbData, DWORD dwSize); void SetData(BYTE* pbData, DWORD dwSize, DWORD dwCodePage); - DWORD GetSize(); - DWORD GetCodePage(); - DWORD GetOffset(); + DWORD GetSize() const; + DWORD GetCodePage() const; + DWORD GetOffset() const; ULONG_PTR m_ulWrittenAt; diff --git a/Source/icon.cpp b/Source/icon.cpp index 59cc2274..74b635f6 100644 --- a/Source/icon.cpp +++ b/Source/icon.cpp @@ -43,13 +43,9 @@ static bool is_valid_header(const void*headerdata) return FIX_ENDIAN_INT16(pGH->wCount) != 0; } -static FILE * open_icon(const TCHAR* filename, IconGroupHeader& igh) +template static WORD read_icon_header(S*f, IconGroupHeader& igh) { - FILE* f = FOPEN(filename, ("rb")); - if (!f) - throw runtime_error("can't open file"); - - if (!fread(&igh, sizeof(IconGroupHeader), 1, f)) + if (!freadall(&igh, sizeof(IconGroupHeader), f)) { fclose(f); throw runtime_error("unable to read header from file"); @@ -57,6 +53,7 @@ static FILE * open_icon(const TCHAR* filename, IconGroupHeader& igh) if (!is_valid_header(&igh)) { + igh.wType = igh.wCount = 0; fclose(f); throw runtime_error("invalid icon file"); } @@ -64,30 +61,31 @@ static FILE * open_icon(const TCHAR* filename, IconGroupHeader& igh) FIX_ENDIAN_INT16_INPLACE(igh.wReserved); FIX_ENDIAN_INT16_INPLACE(igh.wType); FIX_ENDIAN_INT16_INPLACE(igh.wCount); - return f; + return igh.wCount; } -void free_loaded_icon(IconGroup icon) +void free_loaded_icon(IconGroup&icon) { for (IconGroup::size_type i = 0; i < icon.size(); i++) - { delete [] icon[i].data; - } + icon.clear(); } -IconGroup load_icon_res(CResourceEditor* re, WORD id) +IconGroup load_icon_res(CResourceEditor* re, const TCHAR*RT, WORD RN, LANGID RL) { IconGroupHeader* header; IconGroup result; - LPBYTE group = re->GetResource( - RT_GROUP_ICON, id, NSIS_DEFAULT_LANG); - + LPBYTE group = re->GetResource(RT, RN, RL); if (!group) throw runtime_error("can't find icon group"); header = (IconGroupHeader*) group; + // Note: To handle cursors, use CResourceEditor::ExtractIcoCur + if (MAKEINTRESOURCE((size_t) RT) != RT_GROUP_ICON) + throw runtime_error("unsupported type"); + for (WORD i = 0; i < FIX_ENDIAN_INT16(header->wCount); i++) { Icon icon; @@ -99,9 +97,7 @@ IconGroup load_icon_res(CResourceEditor* re, WORD id) memcpy(&icon.meta, &entry->header, sizeof(IconGroupEntry)); WORD rsrc_id = FIX_ENDIAN_INT16(entry->wRsrcId); - - icon.data = re->GetResource(RT_ICON, rsrc_id, NSIS_DEFAULT_LANG); - + icon.data = re->GetResource(RT_ICON, rsrc_id, RL); if (!icon.data) { free_loaded_icon(result); @@ -115,13 +111,14 @@ IconGroup load_icon_res(CResourceEditor* re, WORD id) return result; } -IconGroup load_icon_file(const TCHAR* filename) +IconGroup load_icon_res(CResourceEditor* re, WORD id) { - IconGroupHeader iconHeader; - IconGroup result; + return load_icon_res(re, RT_GROUP_ICON, id, NSIS_DEFAULT_LANG); +} - FILE *file = open_icon(filename, iconHeader); - MANAGE_WITH(file, fclose); +template static IconGroup load_iconimages_from_stream(const IconGroupHeader&iconHeader, S&file) +{ + IconGroup result; for (WORD i = 0; i < iconHeader.wCount; i++) { @@ -129,7 +126,7 @@ IconGroup load_icon_file(const TCHAR* filename) icon.index = i; icon.data = NULL; - if (!fread(&icon.meta, sizeof(IconGroupEntry), 1, file)) + if (!freadall(&icon.meta, sizeof(IconGroupEntry), file)) { free_loaded_icon(result); throw runtime_error("unable to read entry from file"); @@ -143,8 +140,7 @@ IconGroup load_icon_file(const TCHAR* filename) } DWORD iconOffset; - - if (!fread(&iconOffset, sizeof(DWORD), 1, file)) + if (!freadall(&iconOffset, sizeof(DWORD), file)) { free_loaded_icon(result); throw runtime_error("unable to read offset from file"); @@ -154,7 +150,6 @@ IconGroup load_icon_file(const TCHAR* filename) fpos_t pos; fgetpos(file, &pos); - if (fseek(file, iconOffset, SEEK_SET)) { free_loaded_icon(result); @@ -162,14 +157,14 @@ IconGroup load_icon_file(const TCHAR* filename) } icon.data = new BYTE[size]; - - if (!fread(icon.data, size, 1, file)) + if (!freadall(icon.data, size, file)) die: { free_loaded_icon(result); throw runtime_error("unable to read icon from file"); } - fsetpos(file, &pos); + if (fsetpos(file, &pos)) + goto die; result.push_back(icon); } @@ -177,6 +172,39 @@ IconGroup load_icon_file(const TCHAR* filename) return result; } +template static IconGroup load_icon_from_stream(S*file) +{ + IconGroupHeader iconHeader; + read_icon_header(file, iconHeader); + return load_iconimages_from_stream(iconHeader, file); +} + +IconGroup load_icon_file(const TCHAR* filename) +{ + FILE* f = FOPEN(filename, ("rb")); + if (!f) + throw runtime_error("can't open file"); + + MANAGE_WITH(f, fclose); + return load_icon_from_stream(f); +} + +IconGroup load_icon(const TCHAR* filename) +{ + if (CResourceEditor::IsResProtocol(filename)) // Try opening embedded resource + { + CResourceEditor::EXTERNAL x; + if (CResourceEditor::MapExternal(filename, CResourceEditor::TM_ICON, x)) + { + CStdFileStreamOnMemory stream(x.Data, x.cbData); + IconGroup icon = load_icon_from_stream(&stream); + CResourceEditor::FreeExternal(x); + return icon; + } + } + return load_icon_file(filename); +} + typedef struct { unsigned index1; diff --git a/Source/icon.h b/Source/icon.h index 7449766b..17d59bce 100644 --- a/Source/icon.h +++ b/Source/icon.h @@ -62,9 +62,10 @@ typedef struct typedef std::vector IconGroup; +IconGroup load_icon(const TCHAR* filename); IconGroup load_icon_file(const TCHAR* filename); IconGroup load_icon_res(CResourceEditor* re, WORD id); -void free_loaded_icon(IconGroup icon); +void free_loaded_icon(IconGroup&icon); void set_main_icon(CResourceEditor* re, WORD wIconId, IconGroup icon1, IconGroup icon2); diff --git a/Source/script.cpp b/Source/script.cpp index 9b85490a..31c2a7d4 100644 --- a/Source/script.cpp +++ b/Source/script.cpp @@ -1333,7 +1333,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) SCRIPT_MSG(_T("Icon: \"%") NPRIs _T("\"\n"),line.gettoken_str(1)); try { free_loaded_icon(installer_icon); - installer_icon = load_icon_file(line.gettoken_str(1)); + installer_icon = load_icon(line.gettoken_str(1)); } catch (exception& err) { ERROR_MSG(_T("Error while loading icon from \"%") NPRIs _T("\": %") NPRIs _T("\n"), line.gettoken_str(1), CtoTStrParam(err.what())); @@ -2593,7 +2593,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line) SCRIPT_MSG(_T("UninstallIcon: \"%") NPRIs _T("\"\n"),line.gettoken_str(1)); try { free_loaded_icon(uninstaller_icon); - uninstaller_icon = load_icon_file(line.gettoken_str(1)); + uninstaller_icon = load_icon(line.gettoken_str(1)); } catch (exception& err) { ERROR_MSG(_T("Error while loading icon from \"%") NPRIs _T("\": %") NPRIs _T("\n"), line.gettoken_str(1), CtoTStrParam(err.what())); diff --git a/Source/util.h b/Source/util.h index 831a03be..ba99d447 100644 --- a/Source/util.h +++ b/Source/util.h @@ -29,8 +29,8 @@ # include # include #endif - #include +#include extern double my_wtof(const wchar_t *str); extern size_t my_strncpy(TCHAR*Dest, const TCHAR*Src, size_t cchMax); @@ -109,6 +109,49 @@ public: int sane_system(const TCHAR *command); +template static size_t freadall(void*b, size_t cb, S*s) { return cb == fread(b, 1, cb, s) ? cb : 0; } +template static inline size_t freadpod(T&b, S*s) { return freadall(b, sizeof(T), s); } + +struct CStdFileStreamOnMemory +{ + typedef CStdFileStreamOnMemory S_t; + BYTE*m_Base; + size_t m_cb, m_pos; + template CStdFileStreamOnMemory(T*p, size_t cb) : m_Base((BYTE*)p), m_cb(cb), m_pos(0) {} + template static inline bool assignfp(fpos_t*p, V v) + { + if (sizeof(p) >= sizeof(v)) return (*(V*)p = v, true); + UINT t = (UINT) v; + return t == v && sizeof(p) >= sizeof(t) && assignfp(p, t); + } + template static inline void readfp(V&v, const fpos_t*p) { v = sizeof(p) >= sizeof(v) ? *(V*)p : *(UINT*)p; } + friend inline size_t fclose(S_t*s) { return 0; } + friend int fgetpos(S_t*s, fpos_t*pos) { return assignfp(pos, s->m_pos) ? 0 : 1; } + friend int fsetpos(S_t*s, const fpos_t*pos) { size_t v; readfp(v, pos); return v < s->m_cb ? (s->m_pos = v, 0) : 1; } + friend int fseek(S_t*s, long int offset, int origin) + { + if ((unsigned long) offset != (size_t) offset) return 1; // long int will usually fit in our size_t + size_t newpos, invalid = 0; + switch(origin) + { + case SEEK_SET: newpos = (size_t) offset, invalid = offset < 0; break; + case SEEK_CUR: newpos = s->m_pos + offset; break; + case SEEK_END: newpos = s->m_cb + offset; break; + default: ++invalid; + } + if (!(invalid += s->m_cb <= newpos)) s->m_pos = newpos; + return (int) invalid; + } + friend size_t fread(void*b, size_t e, size_t c, S_t*s) + { + size_t cb = e * c, endpos = s->m_pos + cb; + if (endpos < s->m_pos || cb < c) return 0; // EOF/Overflow + if (s->m_cb < endpos) cb = s->m_cb - s->m_pos; // EOF + memcpy(b, s->m_Base + s->m_pos, cb); + return (s->m_pos += cb, cb); + } +}; + #define NSISRT_DEFINEGLOBALS() int g_display_errors=1; FILE *g_output=stdout, *g_errout=stderr void PrintColorFmtErrMsg(const TCHAR *fmtstr, va_list args); void PrintColorFmtMsg(unsigned int type, const TCHAR *fmtstr, va_list args);