xref: /reactos/win32ss/user/user32/misc/exticon.c (revision 682f85ad)
1 /*
2  *	icon extracting
3  *
4  * taken and slightly changed from shell
5  * this should replace the icon extraction code in shell32 and shell16 once
6  * it needs a serious test for compliance with the native API
7  *
8  * Copyright 2000 Juergen Schmied
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <user32.h>
26 
27 #ifndef ARRAY_SIZE
28 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
29 #endif
30 
31 /* Start of Hack section */
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(icon);
34 
35 #include <pshpack1.h>
36 
37 typedef struct
38 {
39     BYTE        bWidth;          /* Width, in pixels, of the image	*/
40     BYTE        bHeight;         /* Height, in pixels, of the image	*/
41     BYTE        bColorCount;     /* Number of colors in image (0 if >=8bpp) */
42     BYTE        bReserved;       /* Reserved ( must be 0)		*/
43     WORD        wPlanes;         /* Color Planes			*/
44     WORD        wBitCount;       /* Bits per pixel			*/
45     DWORD       dwBytesInRes;    /* How many bytes in this resource?	*/
46     DWORD       dwImageOffset;   /* Where in the file is this image?	*/
47 } icoICONDIRENTRY, *LPicoICONDIRENTRY;
48 
49 typedef struct
50 {
51     WORD            idReserved;   /* Reserved (must be 0) */
52     WORD            idType;       /* Resource Type (RES_ICON or RES_CURSOR) */
53     WORD            idCount;      /* How many images */
54     icoICONDIRENTRY idEntries[1]; /* An entry for each image (idCount of 'em) */
55 } icoICONDIR, *LPicoICONDIR;
56 
57 typedef struct
58 {
59     WORD offset;
60     WORD length;
61     WORD flags;
62     WORD id;
63     WORD handle;
64     WORD usage;
65 } NE_NAMEINFO;
66 
67 typedef struct
68 {
69     WORD  type_id;
70     WORD  count;
71     DWORD resloader;
72 } NE_TYPEINFO;
73 
74 #define NE_RSCTYPE_ICON        0x8003
75 #define NE_RSCTYPE_GROUP_ICON  0x800e
76 
77 #include <poppack.h>
78 
79 #if 0
80 static void dumpIcoDirEnty ( LPicoICONDIRENTRY entry )
81 {
82 	TRACE("width = 0x%08x height = 0x%08x\n", entry->bWidth, entry->bHeight);
83 	TRACE("colors = 0x%08x planes = 0x%08x\n", entry->bColorCount, entry->wPlanes);
84 	TRACE("bitcount = 0x%08x bytesinres = 0x%08lx offset = 0x%08lx\n",
85 	entry->wBitCount, entry->dwBytesInRes, entry->dwImageOffset);
86 }
87 static void dumpIcoDir ( LPicoICONDIR entry )
88 {
89 	TRACE("type = 0x%08x count = 0x%08x\n", entry->idType, entry->idCount);
90 }
91 #endif
92 
93 #ifndef WINE
94 DWORD get_best_icon_file_offset(const LPBYTE dir,
95                                 DWORD dwFileSize,
96                                 int cxDesired,
97                                 int cyDesired,
98                                 BOOL bIcon,
99                                 DWORD fuLoad,
100                                 POINT *ptHotSpot);
101 #endif
102 
103 /**********************************************************************
104  *  find_entry_by_id
105  *
106  * Find an entry by id in a resource directory
107  * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
108  */
109 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
110                                                          WORD id, const void *root )
111 {
112     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
113     int min, max, pos;
114 
115     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
116     min = dir->NumberOfNamedEntries;
117     max = min + dir->NumberOfIdEntries - 1;
118     while (min <= max)
119     {
120         pos = (min + max) / 2;
121         if (entry[pos].Id == id)
122             return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry[pos].OffsetToDirectory);
123         if (entry[pos].Id > id) max = pos - 1;
124         else min = pos + 1;
125     }
126     return NULL;
127 }
128 
129 /**********************************************************************
130  *  find_entry_default
131  *
132  * Find a default entry in a resource directory
133  * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
134  */
135 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
136                                                            const void *root )
137 {
138     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
139     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
140     return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->OffsetToDirectory);
141 }
142 
143 /*************************************************************************
144  *				USER32_GetResourceTable
145  */
146 static DWORD USER32_GetResourceTable(LPBYTE peimage,DWORD pesize,LPBYTE *retptr)
147 {
148 	IMAGE_DOS_HEADER	* mz_header;
149 
150 	TRACE("%p %p\n", peimage, retptr);
151 
152 	*retptr = NULL;
153 
154 	mz_header = (IMAGE_DOS_HEADER*) peimage;
155 
156 	if (mz_header->e_magic != IMAGE_DOS_SIGNATURE)
157 	{
158 	  if (mz_header->e_cblp == 1 || mz_header->e_cblp == 2)	/* .ICO or .CUR file ? */
159 	  {
160 	    *retptr = (LPBYTE)-1;	/* ICONHEADER.idType, must be 1 */
161 	    return mz_header->e_cblp;
162 	  }
163 	  else
164 	    return 0; /* failed */
165 	}
166 	if (mz_header->e_lfanew >= pesize) {
167 	    return 0; /* failed, happens with PKZIP DOS Exes for instance. */
168 	}
169 	if (*((DWORD*)(peimage + mz_header->e_lfanew)) == IMAGE_NT_SIGNATURE )
170 	  return IMAGE_NT_SIGNATURE;
171 
172 	if (*((WORD*)(peimage + mz_header->e_lfanew)) == IMAGE_OS2_SIGNATURE )
173 	{
174 	  IMAGE_OS2_HEADER	* ne_header;
175 
176 	  ne_header = (IMAGE_OS2_HEADER*)(peimage + mz_header->e_lfanew);
177 
178 	  if (ne_header->ne_magic != IMAGE_OS2_SIGNATURE)
179 	    return 0;
180 
181 	  if( (ne_header->ne_restab - ne_header->ne_rsrctab) <= sizeof(NE_TYPEINFO) )
182 	    *retptr = (LPBYTE)-1;
183 	  else
184 	    *retptr = peimage + mz_header->e_lfanew + ne_header->ne_rsrctab;
185 
186 	  return IMAGE_OS2_SIGNATURE;
187 	}
188 	return 0; /* failed */
189 }
190 /*************************************************************************
191  *			USER32_LoadResource
192  */
193 static BYTE * USER32_LoadResource( LPBYTE peimage, NE_NAMEINFO* pNInfo, WORD sizeShift, ULONG *uSize)
194 {
195 	TRACE("%p %p 0x%08x\n", peimage, pNInfo, sizeShift);
196 
197 	*uSize = (DWORD)pNInfo->length << sizeShift;
198 	return peimage + ((DWORD)pNInfo->offset << sizeShift);
199 }
200 
201 /*************************************************************************
202  *                      ICO_LoadIcon
203  */
204 static BYTE * ICO_LoadIcon( LPBYTE peimage, LPicoICONDIRENTRY lpiIDE, ULONG *uSize)
205 {
206 	TRACE("%p %p\n", peimage, lpiIDE);
207 
208 	*uSize = lpiIDE->dwBytesInRes;
209 	return peimage + lpiIDE->dwImageOffset;
210 }
211 
212 /*************************************************************************
213  *                      ICO_GetIconDirectory
214  *
215  * Reads .ico file and build phony ICONDIR struct
216  */
217 static BYTE * ICO_GetIconDirectory( LPBYTE peimage, LPicoICONDIR* lplpiID, ULONG *uSize )
218 {
219 	CURSORICONDIR	* lpcid;	/* icon resource in resource-dir format */
220 	CURSORICONDIR	* lpID;		/* icon resource in resource format */
221 	int		i;
222 
223 	TRACE("%p %p\n", peimage, lplpiID);
224 
225 	lpcid = (CURSORICONDIR*)peimage;
226 
227 	if( lpcid->idReserved || (lpcid->idType != 1) || (!lpcid->idCount) )
228 	  return 0;
229 
230 	/* allocate the phony ICONDIR structure */
231         *uSize = FIELD_OFFSET(CURSORICONDIR, idEntries[lpcid->idCount]);
232 	if( (lpID = HeapAlloc(GetProcessHeap(),0, *uSize) ))
233 	{
234 	  /* copy the header */
235 	  lpID->idReserved = lpcid->idReserved;
236 	  lpID->idType = lpcid->idType;
237 	  lpID->idCount = lpcid->idCount;
238 
239 	  /* copy the entries */
240 	  for( i=0; i < lpcid->idCount; i++ )
241 	  {
242             memcpy(&lpID->idEntries[i], &lpcid->idEntries[i], sizeof(CURSORICONDIRENTRY) - 2);
243 	    lpID->idEntries[i].wResId = i;
244 	  }
245 
246 	  *lplpiID = (LPicoICONDIR)peimage;
247 	  return (BYTE *)lpID;
248 	}
249 	return 0;
250 }
251 
252 /*************************************************************************
253  *	ICO_ExtractIconExW		[internal]
254  *
255  * NOTES
256  *  nIcons = 0: returns number of Icons in file
257  *
258  * returns
259  *  invalid file: -1
260  *  failure:0;
261  *  success: number of icons in file (nIcons = 0) or nr of icons retrieved
262  */
263 static UINT ICO_ExtractIconExW(
264 	LPCWSTR lpszExeFileName,
265 	HICON * RetPtr,
266 	INT nIconIndex,
267 	UINT nIcons,
268 	UINT cxDesired,
269 	UINT cyDesired,
270 	UINT *pIconId,
271 	UINT flags)
272 {
273 	UINT		ret = 0;
274 	UINT		cx1, cx2, cy1, cy2;
275 	LPBYTE		pData;
276 	DWORD		sig;
277 	HANDLE		hFile;
278 	UINT16		iconDirCount = 0,iconCount = 0;
279 	LPBYTE		peimage;
280 	HANDLE		fmapping;
281 	DWORD		fsizeh,fsizel;
282 #ifdef __REACTOS__
283     WCHAR		szExpandedExePath[MAX_PATH];
284 #endif
285         WCHAR		szExePath[MAX_PATH];
286         DWORD		dwSearchReturn;
287 
288 	TRACE("%s, %d, %d %p 0x%08x\n", debugstr_w(lpszExeFileName), nIconIndex, nIcons, pIconId, flags);
289 
290 #ifdef __REACTOS__
291     if (RetPtr)
292         *RetPtr = NULL;
293 
294     if (ExpandEnvironmentStringsW(lpszExeFileName, szExpandedExePath, ARRAY_SIZE(szExpandedExePath)))
295         lpszExeFileName = szExpandedExePath;
296 #endif
297 
298         dwSearchReturn = SearchPathW(NULL, lpszExeFileName, NULL, ARRAY_SIZE(szExePath), szExePath, NULL);
299         if ((dwSearchReturn == 0) || (dwSearchReturn > ARRAY_SIZE(szExePath)))
300         {
301             WARN("File %s not found or path too long\n", debugstr_w(lpszExeFileName));
302             return -1;
303         }
304 
305 	hFile = CreateFileW(szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
306 	if (hFile == INVALID_HANDLE_VALUE) return ret;
307 	fsizel = GetFileSize(hFile,&fsizeh);
308 
309 	/* Map the file */
310 	fmapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY | SEC_COMMIT, 0, 0, NULL);
311 	CloseHandle(hFile);
312 	if (!fmapping)
313 	{
314           WARN("CreateFileMapping error %ld\n", GetLastError() );
315 	  return 0xFFFFFFFF;
316 	}
317 
318 	if (!(peimage = MapViewOfFile(fmapping, FILE_MAP_READ, 0, 0, 0)))
319 	{
320           WARN("MapViewOfFile error %ld\n", GetLastError() );
321 	  CloseHandle(fmapping);
322 	  return 0xFFFFFFFF;
323 	}
324 	CloseHandle(fmapping);
325 
326 	cx1 = LOWORD(cxDesired);
327 	cx2 = HIWORD(cxDesired);
328 	cy1 = LOWORD(cyDesired);
329 	cy2 = HIWORD(cyDesired);
330 
331 	if (pIconId) /* Invalidate first icon identifier */
332 		*pIconId = 0xFFFFFFFF;
333 
334 	if (!pIconId) /* if no icon identifier array present use the icon handle array as intermediate storage */
335 	  pIconId = (UINT*)RetPtr;
336 
337 	sig = USER32_GetResourceTable(peimage, fsizel, &pData);
338 
339 /* NE exe/dll */
340 	if (sig==IMAGE_OS2_SIGNATURE)
341 	{
342 	  BYTE		*pCIDir = 0;
343 	  NE_TYPEINFO	*pTInfo = (NE_TYPEINFO*)(pData + 2);
344 	  NE_NAMEINFO	*pIconStorage = NULL;
345 	  NE_NAMEINFO	*pIconDir = NULL;
346 	  LPicoICONDIR	lpiID = NULL;
347 	  ULONG		uSize = 0;
348 
349           TRACE("-- OS2/icon Signature (0x%08x)\n", sig);
350 
351 	  if (pData == (BYTE*)-1)
352 	  {
353 	    pCIDir = ICO_GetIconDirectory(peimage, &lpiID, &uSize);	/* check for .ICO file */
354 	    if (pCIDir)
355 	    {
356 	      iconDirCount = 1; iconCount = lpiID->idCount;
357               TRACE("-- icon found %p 0x%08x 0x%08x 0x%08x\n", pCIDir, uSize, iconDirCount, iconCount);
358 	    }
359 	  }
360 	  else while (pTInfo->type_id && !(pIconStorage && pIconDir))
361 	  {
362 	    if (pTInfo->type_id == NE_RSCTYPE_GROUP_ICON)	/* find icon directory and icon repository */
363 	    {
364 	      iconDirCount = pTInfo->count;
365 	      pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
366 	      TRACE("\tfound directory - %i icon families\n", iconDirCount);
367 	    }
368 	    if (pTInfo->type_id == NE_RSCTYPE_ICON)
369 	    {
370 	      iconCount = pTInfo->count;
371 	      pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
372 	      TRACE("\ttotal icons - %i\n", iconCount);
373 	    }
374 	    pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
375 	  }
376 
377 	  if ((pIconStorage && pIconDir) || lpiID)	  /* load resources and create icons */
378 	  {
379 	    if (nIcons == 0)
380 	    {
381 	      ret = iconDirCount;
382               if (lpiID)	/* *.ico file, deallocate heap pointer*/
383 	        HeapFree(GetProcessHeap(), 0, pCIDir);
384 	    }
385 	    else if (nIconIndex < iconDirCount)
386 	    {
387 	      UINT16   i, icon;
388 	      if (nIcons > iconDirCount - nIconIndex)
389 	        nIcons = iconDirCount - nIconIndex;
390 
391 	      for (i = 0; i < nIcons; i++)
392 	      {
393 	        /* .ICO files have only one icon directory */
394 	        if (lpiID == NULL)	/* not *.ico */
395 	          pCIDir = USER32_LoadResource(peimage, pIconDir + i + nIconIndex, *(WORD*)pData, &uSize);
396 	        pIconId[i] = LookupIconIdFromDirectoryEx(pCIDir, TRUE, cx1, cy1, flags);
397                 if (cx2 && cy2) pIconId[++i] = LookupIconIdFromDirectoryEx(pCIDir, TRUE,  cx2, cy2, flags);
398 	      }
399               if (lpiID)	/* *.ico file, deallocate heap pointer*/
400 	        HeapFree(GetProcessHeap(), 0, pCIDir);
401 
402 	      for (icon = 0; icon < nIcons; icon++)
403 	      {
404 	        pCIDir = NULL;
405 	        if (lpiID)
406 	          pCIDir = ICO_LoadIcon(peimage, lpiID->idEntries + (int)pIconId[icon], &uSize);
407 	        else
408 	          for (i = 0; i < iconCount; i++)
409 	            if (pIconStorage[i].id == ((int)pIconId[icon] | 0x8000) )
410 	              pCIDir = USER32_LoadResource(peimage, pIconStorage + i, *(WORD*)pData, &uSize);
411 
412 	        if (pCIDir)
413                 {
414 	          RetPtr[icon] = CreateIconFromResourceEx(pCIDir, uSize, TRUE, 0x00030000,
415                                                                  cx1, cy1, flags);
416                   if (cx2 && cy2)
417                       RetPtr[++icon] = CreateIconFromResourceEx(pCIDir, uSize, TRUE, 0x00030000,
418                                                                        cx2, cy2, flags);
419                 }
420 	        else
421 	          RetPtr[icon] = 0;
422 	      }
423 	      ret = icon;	/* return number of retrieved icons */
424 	    }
425 	  }
426 	}
427     else
428     if (sig == 1 || sig == 2) /* .ICO or .CUR file */
429     {
430         TRACE("-- icon Signature (0x%08x)\n", sig);
431 
432         if (pData == (BYTE*)-1)
433         {
434             INT cx[2] = {cx1, cx2}, cy[2] = {cy1, cy2};
435             INT index;
436 
437             for(index = 0; index < (cx2 || cy2 ? 2 : 1); index++)
438             {
439                 DWORD dataOffset;
440                 LPBYTE imageData;
441                 POINT hotSpot;
442                 LPICONIMAGE entry;
443 
444                 dataOffset = get_best_icon_file_offset(peimage, fsizel, cx[index], cy[index], sig == 1, flags, sig == 1 ? NULL : &hotSpot);
445 
446                 if (dataOffset)
447                 {
448                     HICON icon;
449                     WORD *cursorData = NULL;
450 
451                     imageData = peimage + dataOffset;
452                     entry = (LPICONIMAGE)(imageData);
453 
454                     if(sig == 2)
455                     {
456                         /* we need to prepend the bitmap data with hot spots for CreateIconFromResourceEx */
457                         cursorData = HeapAlloc(GetProcessHeap(), 0, entry->icHeader.biSizeImage + 2 * sizeof(WORD));
458 
459                         if(!cursorData)
460                             continue;
461 
462                         cursorData[0] = hotSpot.x;
463                         cursorData[1] = hotSpot.y;
464 
465                         memcpy(cursorData + 2, imageData, entry->icHeader.biSizeImage);
466 
467                         imageData = (LPBYTE)cursorData;
468                     }
469 
470                     icon = CreateIconFromResourceEx(imageData, entry->icHeader.biSizeImage, sig == 1, 0x00030000, cx[index], cy[index], flags);
471 
472                     HeapFree(GetProcessHeap(), 0, cursorData);
473 
474                     if (icon)
475                     {
476                         if (RetPtr)
477                             RetPtr[index] = icon;
478                         else
479                             DestroyIcon(icon);
480 
481                         iconCount = 1;
482                         break;
483                     }
484                 }
485             }
486         }
487         ret = iconCount;	/* return number of retrieved icons */
488     }
489 /* end ico file */
490 
491 /* exe/dll */
492 	else if( sig == IMAGE_NT_SIGNATURE )
493 	{
494         BYTE *idata, *igdata;
495         const IMAGE_RESOURCE_DIRECTORY *rootresdir, *iconresdir, *icongroupresdir;
496         const IMAGE_RESOURCE_DATA_ENTRY *idataent, *igdataent;
497         const IMAGE_RESOURCE_DIRECTORY_ENTRY *xresent;
498         ULONG size;
499         UINT i;
500 
501         rootresdir = RtlImageDirectoryEntryToData((HMODULE)peimage, FALSE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size);
502         if (!rootresdir)
503         {
504             WARN("haven't found section for resource directory.\n");
505             goto end;
506         }
507 
508 	  /* search for the group icon directory */
509 	  if (!(icongroupresdir = find_entry_by_id(rootresdir, LOWORD(RT_GROUP_ICON), rootresdir)))
510 	  {
511 	    WARN("No Icongroupresourcedirectory!\n");
512 	    goto end;		/* failure */
513 	  }
514 	  iconDirCount = icongroupresdir->NumberOfNamedEntries + icongroupresdir->NumberOfIdEntries;
515 
516 	  /* only number of icons requested */
517 	  if( !pIconId )
518 	  {
519 	    ret = iconDirCount;
520 	    goto end;		/* success */
521 	  }
522 
523 	  if( nIconIndex < 0 )
524 	  {
525 	    /* search resource id */
526 	    int n = 0;
527 	    int iId = abs(nIconIndex);
528 	    const IMAGE_RESOURCE_DIRECTORY_ENTRY* xprdeTmp = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(icongroupresdir+1);
529 
530 	    while(n<iconDirCount && xprdeTmp)
531 	    {
532               if(xprdeTmp->Id ==  iId)
533               {
534                   nIconIndex = n;
535                   break;
536               }
537               n++;
538               xprdeTmp++;
539 	    }
540 	    if (nIconIndex < 0)
541 	    {
542 	      WARN("resource id %d not found\n", iId);
543 	      goto end;		/* failure */
544 	    }
545 	  }
546 	  else
547 	  {
548 	    /* check nIconIndex to be in range */
549 	    if (nIconIndex >= iconDirCount)
550 	    {
551 	      WARN("nIconIndex %d is larger than iconDirCount %d\n",nIconIndex,iconDirCount);
552 	      goto end;		/* failure */
553 	    }
554 	  }
555 
556 	  /* assure we don't get too much */
557 	  if( nIcons > iconDirCount - nIconIndex )
558 	    nIcons = iconDirCount - nIconIndex;
559 
560 	  /* starting from specified index */
561 	  xresent = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(icongroupresdir+1) + nIconIndex;
562 
563 	  for (i=0; i < nIcons; i++,xresent++)
564 	  {
565 	    const IMAGE_RESOURCE_DIRECTORY *resdir;
566 
567 	    /* go down this resource entry, name */
568             resdir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)rootresdir + xresent->OffsetToDirectory);
569 
570 	    /* default language (0) */
571 	    resdir = find_entry_default(resdir,rootresdir);
572 	    igdataent = (const IMAGE_RESOURCE_DATA_ENTRY*)resdir;
573 
574 	    /* lookup address in mapped image for virtual address */
575         igdata = RtlImageRvaToVa(RtlImageNtHeader((HMODULE)peimage), (HMODULE)peimage, igdataent->OffsetToData, NULL);
576         if (!igdata)
577 	    {
578 	      FIXME("no matching real address for icongroup!\n");
579 	      goto end;	/* failure */
580 	    }
581 	    pIconId[i] = LookupIconIdFromDirectoryEx(igdata, TRUE, cx1, cy1, flags);
582             if (cx2 && cy2) pIconId[++i] = LookupIconIdFromDirectoryEx(igdata, TRUE, cx2, cy2, flags);
583 	  }
584 
585 	  if (!(iconresdir=find_entry_by_id(rootresdir,LOWORD(RT_ICON),rootresdir)))
586 	  {
587 	    WARN("No Iconresourcedirectory!\n");
588 	    goto end;		/* failure */
589 	  }
590 
591 	  for (i=0; i<nIcons; i++)
592 	  {
593 	    const IMAGE_RESOURCE_DIRECTORY *xresdir;
594 	    xresdir = find_entry_by_id(iconresdir, LOWORD(pIconId[i]), rootresdir);
595             if( !xresdir )
596             {
597               WARN("icon entry %d not found\n", LOWORD(pIconId[i]));
598 	      RetPtr[i]=0;
599 	      continue;
600             }
601 	    xresdir = find_entry_default(xresdir, rootresdir);
602 	    idataent = (const IMAGE_RESOURCE_DATA_ENTRY*)xresdir;
603 
604         idata = RtlImageRvaToVa(RtlImageNtHeader((HMODULE)peimage), (HMODULE)peimage, idataent->OffsetToData, NULL);
605         if (!idata)
606 	    {
607 	      WARN("no matching real address found for icondata!\n");
608 	      RetPtr[i]=0;
609 	      continue;
610 	    }
611 	    RetPtr[i] = CreateIconFromResourceEx(idata, idataent->Size, TRUE, 0x00030000, cx1, cy1, flags);
612             if (cx2 && cy2)
613                 RetPtr[++i] = CreateIconFromResourceEx(idata, idataent->Size, TRUE, 0x00030000, cx2, cy2, flags);
614 	  }
615 	  ret = i;	/* return number of retrieved icons */
616 	}			/* if(sig == IMAGE_NT_SIGNATURE) */
617 
618 end:
619 	UnmapViewOfFile(peimage);	/* success */
620 	return ret;
621 }
622 
623 /***********************************************************************
624  *           PrivateExtractIconsW			[USER32.@]
625  *
626  * NOTES
627  *  If HIWORD(sizeX) && HIWORD(sizeY) 2 * ((nIcons + 1) MOD 2) icons are
628  *  returned, with the LOWORD size icon first and the HIWORD size icon
629  *  second.
630  *  Also the Windows equivalent does extract icons in a strange way if
631  *  nIndex is negative. Our implementation treats a negative nIndex as
632  *  looking for that resource identifier for the first icon to retrieve.
633  *
634  * FIXME:
635  *  should also support 16 bit EXE + DLLs, cursor and animated cursor as
636  *  well as bitmap files.
637  */
638 
639 UINT WINAPI PrivateExtractIconsW (
640 	LPCWSTR lpwstrFile,
641 	int nIndex,
642 	int sizeX,
643 	int sizeY,
644 	HICON * phicon, /* [out] pointer to array of nIcons HICON handles */
645 	UINT* pIconId,  /* [out] pointer to array of nIcons icon identifiers or NULL */
646 	UINT nIcons,    /* [in] number of icons to retrieve */
647 	UINT flags )    /* [in] LR_* flags used by LoadImage */
648 {
649 	TRACE("%s %d %dx%d %p %p %d 0x%08x\n",
650 	      debugstr_w(lpwstrFile), nIndex, sizeX, sizeY, phicon, pIconId, nIcons, flags);
651 
652 	if ((nIcons & 1) && HIWORD(sizeX) && HIWORD(sizeY))
653 	{
654 	  WARN("Uneven number %d of icons requested for small and large icons!\n", nIcons);
655 	}
656 	return ICO_ExtractIconExW(lpwstrFile, phicon, nIndex, nIcons, sizeX, sizeY, pIconId, flags);
657 }
658 
659 /***********************************************************************
660  *           PrivateExtractIconsA			[USER32.@]
661  */
662 
663 UINT WINAPI PrivateExtractIconsA (
664 	LPCSTR lpstrFile,
665 	int nIndex,
666 	int sizeX,
667 	int sizeY,
668 	HICON * phicon, /* [out] pointer to array of nIcons HICON handles */
669 	UINT* piconid,  /* [out] pointer to array of nIcons icon identifiers or NULL */
670 	UINT nIcons,    /* [in] number of icons to retrieve */
671 	UINT flags )    /* [in] LR_* flags used by LoadImage */
672 {
673     UINT ret;
674     INT len = MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, NULL, 0);
675     LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
676 #ifdef __REACTOS__
677     if (lpwstrFile == NULL)
678         return 0;
679 #endif
680 
681     MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, lpwstrFile, len);
682     ret = PrivateExtractIconsW(lpwstrFile, nIndex, sizeX, sizeY, phicon, piconid, nIcons, flags);
683 
684     HeapFree(GetProcessHeap(), 0, lpwstrFile);
685     return ret;
686 }
687 
688 /***********************************************************************
689  *           PrivateExtractIconExW			[USER32.@]
690  * NOTES
691  *  if nIndex == -1 it returns the number of icons in any case !!!
692  */
693 UINT WINAPI PrivateExtractIconExW (
694 	LPCWSTR lpwstrFile,
695 	int nIndex,
696 	HICON * phIconLarge,
697 	HICON * phIconSmall,
698 	UINT nIcons )
699 {
700 	DWORD cyicon, cysmicon, cxicon, cxsmicon;
701 	UINT ret = 0;
702 
703 	TRACE("%s %d %p %p %d\n",
704 	debugstr_w(lpwstrFile),nIndex,phIconLarge, phIconSmall, nIcons);
705 
706 	if (nIndex == -1)
707 	  /* get the number of icons */
708 	  return ICO_ExtractIconExW(lpwstrFile, NULL, 0, 0, 0, 0, NULL, LR_DEFAULTCOLOR);
709 
710 	if (nIcons == 1 && phIconSmall && phIconLarge)
711 	{
712 	  HICON hIcon[2];
713 	  cxicon = GetSystemMetrics(SM_CXICON);
714 	  cyicon = GetSystemMetrics(SM_CYICON);
715 	  cxsmicon = GetSystemMetrics(SM_CXSMICON);
716 	  cysmicon = GetSystemMetrics(SM_CYSMICON);
717 
718           ret = ICO_ExtractIconExW(lpwstrFile, hIcon, nIndex, 2, cxicon | (cxsmicon<<16),
719 	                           cyicon | (cysmicon<<16), NULL, LR_DEFAULTCOLOR);
720 	  *phIconLarge = hIcon[0];
721 	  *phIconSmall = hIcon[1];
722  	  return ret;
723 	}
724 
725 	if (phIconSmall)
726 	{
727 	  /* extract n small icons */
728 	  cxsmicon = GetSystemMetrics(SM_CXSMICON);
729 	  cysmicon = GetSystemMetrics(SM_CYSMICON);
730 	  ret = ICO_ExtractIconExW(lpwstrFile, phIconSmall, nIndex, nIcons, cxsmicon,
731 	                           cysmicon, NULL, LR_DEFAULTCOLOR);
732 	}
733        if (phIconLarge)
734 	{
735 	  /* extract n large icons */
736 	  cxicon = GetSystemMetrics(SM_CXICON);
737 	  cyicon = GetSystemMetrics(SM_CYICON);
738          ret = ICO_ExtractIconExW(lpwstrFile, phIconLarge, nIndex, nIcons, cxicon,
739 	                           cyicon, NULL, LR_DEFAULTCOLOR);
740 	}
741 	return ret;
742 }
743 
744 /***********************************************************************
745  *           PrivateExtractIconExA			[USER32.@]
746  */
747 UINT WINAPI PrivateExtractIconExA (
748 	LPCSTR lpstrFile,
749 	int nIndex,
750 	HICON * phIconLarge,
751 	HICON * phIconSmall,
752 	UINT nIcons )
753 {
754 	UINT ret;
755 	INT len = MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, NULL, 0);
756 	LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
757 #ifdef __REACTOS__
758     if (lpwstrFile == NULL)
759         return 0;
760 #endif
761 
762 	TRACE("%s %d %p %p %d\n", lpstrFile, nIndex, phIconLarge, phIconSmall, nIcons);
763 
764 	MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, lpwstrFile, len);
765 	ret = PrivateExtractIconExW(lpwstrFile, nIndex, phIconLarge, phIconSmall, nIcons);
766 	HeapFree(GetProcessHeap(), 0, lpwstrFile);
767 	return ret;
768 }
769