/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2009 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "..\..\core\commonheaders.h" #ifdef _ASSERT #undef _ASSERT #endif #define _ASSERT(n) // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/introductiontoresources/resourcereference/resourcestructures/newheader.asp typedef struct { WORD Reserved; WORD ResType; WORD ResCount; } NEWHEADER; #define MAGIC_ICON 0 #define MAGIC_ICO1 1 #define MAGIC_CUR 2 #define MAGIC_BMP ((WORD)'B'+((WORD)'M'<<8)) #define MAGIC_ANI1 ((WORD)'R'+((WORD)'I'<<8)) #define MAGIC_ANI2 ((WORD)'F'+((WORD)'F'<<8)) #define MAGIC_ANI3 ((WORD)'A'+((WORD)'C'<<8)) #define MAGIC_ANI4 ((WORD)'O'+((WORD)'N'<<8)) #define VER30 0x00030000 void* _RelativeVirtualAddresstoPtr(IMAGE_DOS_HEADER* pDosHeader, DWORD rva) { IMAGE_NT_HEADERS* pPE = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader + pDosHeader->e_lfanew); IMAGE_SECTION_HEADER* pSection = IMAGE_FIRST_SECTION( pPE ); int i; for (i = 0; i < pPE->FileHeader.NumberOfSections; i++) { IMAGE_SECTION_HEADER* cSection = &pSection[i]; DWORD size = cSection->Misc.VirtualSize ? cSection->Misc.VirtualSize : cSection->SizeOfRawData; if (rva >= cSection->VirtualAddress && rva < cSection->VirtualAddress + size) return (LPBYTE)pDosHeader + cSection->PointerToRawData + (rva - cSection->VirtualAddress); } return NULL; } void* _GetResourceTable(IMAGE_DOS_HEADER* pDosHeader) { IMAGE_NT_HEADERS* pPE = (IMAGE_NT_HEADERS*)((BYTE*)pDosHeader + pDosHeader->e_lfanew); if (pPE->Signature != IMAGE_NT_SIGNATURE) return NULL; if (pPE->FileHeader.SizeOfOptionalHeader < 2) return NULL; // The DataDirectory is an array of 16 structures. // Each array entry has a predefined meaning for what it refers to. switch (pPE->OptionalHeader.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: if (pPE->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER32)) return _RelativeVirtualAddresstoPtr(pDosHeader, ((PIMAGE_NT_HEADERS32)pPE)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); break; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: if (pPE->FileHeader.SizeOfOptionalHeader >= sizeof(IMAGE_OPTIONAL_HEADER64)) return _RelativeVirtualAddresstoPtr(pDosHeader, ((PIMAGE_NT_HEADERS64)pPE)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); break; } return NULL; } IMAGE_RESOURCE_DIRECTORY_ENTRY* _FindResourceBase(void* prt, int resType, int* pCount) { IMAGE_RESOURCE_DIRECTORY* pDir = (IMAGE_RESOURCE_DIRECTORY*)prt; IMAGE_RESOURCE_DIRECTORY_ENTRY* pRes; int i, count; *pCount = 0; count = pDir->NumberOfIdEntries + pDir->NumberOfNamedEntries; pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1); for (i = 0; i < count; i++) if (pRes[i].Name == (DWORD)resType) break; if (i == count) return NULL; pDir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt + (pRes[i].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY)); count = pDir->NumberOfIdEntries + pDir->NumberOfNamedEntries; *pCount = count; pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1); return pRes; } int _FindResourceCount(void* prt, int resType) { int count; _FindResourceBase(prt, resType, &count); return count; } void* _FindResource(IMAGE_DOS_HEADER* pDosHeader, void* prt, int resIndex, int resType, DWORD* pcbSize) { int count, index = 0; IMAGE_RESOURCE_DIRECTORY_ENTRY* pRes; IMAGE_RESOURCE_DATA_ENTRY* pEntry; pRes = _FindResourceBase(prt, resType, &count); if (resIndex < 0) { for (index = 0; index < count; index++) if (pRes[index].Name == (DWORD)(-resIndex)) break; } else index = resIndex; if (index >= count) return NULL; if (pRes[index].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) { IMAGE_RESOURCE_DIRECTORY* pDir; pDir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt + (pRes[index].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY) ); pRes = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pDir+1); index = 0; } if ( pRes[index].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY ) return NULL; pEntry = (IMAGE_RESOURCE_DATA_ENTRY*)((LPBYTE)prt + pRes[index].OffsetToData); *pcbSize = pEntry->Size; return _RelativeVirtualAddresstoPtr(pDosHeader, pEntry->OffsetToData); } UINT _ExtractFromExe(HANDLE hFile, int iconIndex, int cxIconSize, int cyIconSize, HICON *phicon, UINT flags) { int retval = 0; DWORD fileLen = GetFileSize(hFile, NULL); HANDLE hFileMap = INVALID_HANDLE_VALUE, pFile = NULL; IMAGE_DOS_HEADER* pDosHeader; void* pRes; DWORD cbSize = 0; NEWHEADER* pIconDir; int idIcon; LPBITMAPINFOHEADER pIcon; // UINT res = 0; hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hFileMap == NULL) goto cleanup; pFile = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); if (pFile == NULL) goto cleanup; pDosHeader = (IMAGE_DOS_HEADER*)(void*)pFile; if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) goto cleanup; if (pDosHeader->e_lfanew <= 0) goto cleanup; if ((DWORD)(pDosHeader->e_lfanew) >= fileLen) goto cleanup; pRes = _GetResourceTable(pDosHeader); if (!pRes) goto cleanup; if (!phicon) { retval = _FindResourceCount(pRes, (int)RT_GROUP_ICON); goto cleanup; } pIconDir = (NEWHEADER*)_FindResource(pDosHeader, pRes, iconIndex, (int)RT_GROUP_ICON, &cbSize); if (!pIconDir) goto cleanup; if (pIconDir->Reserved || pIconDir->ResType != RES_ICON) goto cleanup; idIcon = LookupIconIdFromDirectoryEx((LPBYTE)pIconDir, TRUE, cxIconSize, cyIconSize, flags); pIcon = (LPBITMAPINFOHEADER)_FindResource(pDosHeader, pRes, -idIcon, (int)RT_ICON, &cbSize); if (!pIcon) goto cleanup; if ( pIcon->biSize != sizeof(BITMAPINFOHEADER) && pIcon->biSize != sizeof(BITMAPCOREHEADER)) { _ASSERT(0); goto cleanup; } *phicon = CreateIconFromResourceEx((LPBYTE)pIcon, cbSize, TRUE, VER30, cxIconSize, cyIconSize, flags); retval = 1; cleanup: if (pFile) UnmapViewOfFile(pFile); if (hFileMap != INVALID_HANDLE_VALUE) CloseHandle(hFileMap); return retval; } UINT _ExtractFromICO( LPCTSTR pFileName, int iconIndex, int cxIcon, int cyIcon, HICON* phicon, UINT flags ) { HICON hicon; if ( iconIndex >= 1 ) return 0; // do we just want a count? if (!phicon) return 1; flags |= LR_LOADFROMFILE; hicon = (HICON)LoadImage( NULL, pFileName, IMAGE_ICON, cxIcon, cyIcon, flags ); if (!hicon) return 0; *phicon = hicon; return 1; } UINT _ExtractIconEx(LPCTSTR lpszFile, int iconIndex, int cxIcon, int cyIcon, HICON *phicon, UINT flags) { HANDLE hFile; WORD magic[6]; DWORD read = 0; UINT res = 0; if (cxIcon == GetSystemMetrics(SM_CXICON) && cyIcon == GetSystemMetrics(SM_CYICON)) res = ExtractIconEx(lpszFile, iconIndex, phicon, NULL, 1); else if (cxIcon == GetSystemMetrics(SM_CXSMICON) && cyIcon == GetSystemMetrics(SM_CYSMICON)) res = ExtractIconEx(lpszFile, iconIndex, NULL, phicon, 1); else if (cxIcon == 0 || cyIcon == 0) res = ExtractIconEx(lpszFile, iconIndex, NULL, phicon, 1); // check if the api succeded, if not try our method too if (res) return res; hFile = CreateFile(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) return 0; // failed to read file signature if ( !ReadFile(hFile, &magic, sizeof(magic), &read, NULL ) || (read != sizeof(magic))) { CloseHandle(hFile); return 0; } switch ( magic[0] ) { case IMAGE_DOS_SIGNATURE: res = _ExtractFromExe(hFile, iconIndex, cxIcon, cyIcon, phicon, flags); break; case MAGIC_ANI1: // ani cursors are RIFF file of type 'ACON' if (magic[1] == MAGIC_ANI2 && magic[4] == MAGIC_ANI3 && magic[5] == MAGIC_ANI4) res = _ExtractFromICO(lpszFile, iconIndex, cxIcon, cyIcon, phicon, flags); break; case MAGIC_ICON: if ((magic[1] == MAGIC_ICO1 || magic[1] == MAGIC_CUR ) && magic[2] >= 1 ) res = _ExtractFromICO(lpszFile, iconIndex, cxIcon, cyIcon, phicon, flags); break; } CloseHandle(hFile); return res; }