exticon.c 23.1 KB
Newer Older
1 2 3 4 5
/*
 *	icon extracting
 *
 * taken and slightly changed from shell
 * 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 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * Copyright 2000 Juergen Schmied
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 */
24 25 26

#include "config.h"

27
#include <stdarg.h>
28
#include <string.h>
29
#include <stdlib.h>	/* abs() */
30
#include <sys/types.h>
31 32 33
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
34

35 36
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
37
#include "windef.h"
38
#include "winbase.h"
39 40 41
#include "winerror.h"
#include "wingdi.h"
#include "winuser.h"
42
#include "wine/winbase16.h"
43
#include "cursoricon.h"
44
#include "wine/debug.h"
45

46
WINE_DEFAULT_DEBUG_CHANNEL(icon);
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

#include "pshpack1.h"

typedef struct
{
    BYTE        bWidth;          /* Width, in pixels, of the image	*/
    BYTE        bHeight;         /* Height, in pixels, of the image	*/
    BYTE        bColorCount;     /* Number of colors in image (0 if >=8bpp) */
    BYTE        bReserved;       /* Reserved ( must be 0)		*/
    WORD        wPlanes;         /* Color Planes			*/
    WORD        wBitCount;       /* Bits per pixel			*/
    DWORD       dwBytesInRes;    /* How many bytes in this resource?	*/
    DWORD       dwImageOffset;   /* Where in the file is this image?	*/
} icoICONDIRENTRY, *LPicoICONDIRENTRY;

typedef struct
{
    WORD            idReserved;   /* Reserved (must be 0) */
    WORD            idType;       /* Resource Type (RES_ICON or RES_CURSOR) */
    WORD            idCount;      /* How many images */
    icoICONDIRENTRY idEntries[1]; /* An entry for each image (idCount of 'em) */
} icoICONDIR, *LPicoICONDIR;

#include "poppack.h"

#if 0
static void dumpIcoDirEnty ( LPicoICONDIRENTRY entry )
74
{
75 76
	TRACE("width = 0x%08x height = 0x%08x\n", entry->bWidth, entry->bHeight);
	TRACE("colors = 0x%08x planes = 0x%08x\n", entry->bColorCount, entry->wPlanes);
77
	TRACE("bitcount = 0x%08x bytesinres = 0x%08lx offset = 0x%08lx\n",
78 79 80
	entry->wBitCount, entry->dwBytesInRes, entry->dwImageOffset);
}
static void dumpIcoDir ( LPicoICONDIR entry )
81
{
82 83 84
	TRACE("type = 0x%08x count = 0x%08x\n", entry->idType, entry->idCount);
}
#endif
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

/**********************************************************************
 *  find_entry_by_id
 *
 * Find an entry by id in a resource directory
 * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
 */
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                         WORD id, const void *root )
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
    int min, max, pos;

    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
    min = dir->NumberOfNamedEntries;
    max = min + dir->NumberOfIdEntries - 1;
    while (min <= max)
    {
        pos = (min + max) / 2;
104
        if (entry[pos].u1.s2.Id == id)
105
            return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
106
        if (entry[pos].u1.s2.Id > id) max = pos - 1;
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
        else min = pos + 1;
    }
    return NULL;
}

/**********************************************************************
 *  find_entry_default
 *
 * Find a default entry in a resource directory
 * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
 */
static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                           const void *root )
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
123
    return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
124 125
}

126 127 128
/*************************************************************************
 *				USER32_GetResourceTable
 */
129
static DWORD USER32_GetResourceTable(LPBYTE peimage,DWORD pesize,LPBYTE *retptr)
130 131 132
{
	IMAGE_DOS_HEADER	* mz_header;

133
	TRACE("%p %p\n", peimage, retptr);
134 135 136 137

	*retptr = NULL;

	mz_header = (IMAGE_DOS_HEADER*) peimage;
138

139 140 141 142 143 144 145 146 147 148
	if (mz_header->e_magic != IMAGE_DOS_SIGNATURE)
	{
	  if (mz_header->e_cblp == 1)	/* .ICO file ? */
	  {
	    *retptr = (LPBYTE)-1;	/* ICONHEADER.idType, must be 1 */
	    return 1;
	  }
	  else
	    return 0; /* failed */
	}
149 150 151
	if (mz_header->e_lfanew >= pesize) {
	    return 0; /* failed, happens with PKZIP DOS Exes for instance. */
	}
152 153 154 155 156 157 158 159
	if (*((DWORD*)(peimage + mz_header->e_lfanew)) == IMAGE_NT_SIGNATURE )
	  return IMAGE_NT_SIGNATURE;

	if (*((WORD*)(peimage + mz_header->e_lfanew)) == IMAGE_OS2_SIGNATURE )
	{
	  IMAGE_OS2_HEADER	* ne_header;

	  ne_header = (IMAGE_OS2_HEADER*)(peimage + mz_header->e_lfanew);
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	  if (ne_header->ne_magic != IMAGE_OS2_SIGNATURE)
	    return 0;

	  if( (ne_header->ne_restab - ne_header->ne_rsrctab) <= sizeof(NE_TYPEINFO) )
	    *retptr = (LPBYTE)-1;
	  else
	    *retptr = peimage + mz_header->e_lfanew + ne_header->ne_rsrctab;

	  return IMAGE_OS2_SIGNATURE;
	}
	return 0; /* failed */
}
/*************************************************************************
 *			USER32_LoadResource
 */
static BYTE * USER32_LoadResource( LPBYTE peimage, NE_NAMEINFO* pNInfo, WORD sizeShift, ULONG *uSize)
{
	TRACE("%p %p 0x%08x\n", peimage, pNInfo, sizeShift);

	*uSize = (DWORD)pNInfo->length << sizeShift;
	return peimage + ((DWORD)pNInfo->offset << sizeShift);
}

/*************************************************************************
 *                      ICO_LoadIcon
 */
static BYTE * ICO_LoadIcon( LPBYTE peimage, LPicoICONDIRENTRY lpiIDE, ULONG *uSize)
{
	TRACE("%p %p\n", peimage, lpiIDE);

	*uSize = lpiIDE->dwBytesInRes;
	return peimage + lpiIDE->dwImageOffset;
}

/*************************************************************************
 *                      ICO_GetIconDirectory
 *
 * Reads .ico file and build phony ICONDIR struct
 * see http://www.microsoft.com/win32dev/ui/icons.htm
 */
#define HEADER_SIZE		(sizeof(CURSORICONDIR) - sizeof (CURSORICONDIRENTRY))
#define HEADER_SIZE_FILE	(sizeof(icoICONDIR) - sizeof (icoICONDIRENTRY))

204
static BYTE * ICO_GetIconDirectory( LPBYTE peimage, LPicoICONDIR* lplpiID, ULONG *uSize )
205 206 207 208 209
{
	CURSORICONDIR	* lpcid;	/* icon resource in resource-dir format */
	CURSORICONDIR	* lpID;		/* icon resource in resource format */
	int		i;

210 211
	TRACE("%p %p\n", peimage, lplpiID);

212 213 214 215 216 217 218 219 220 221 222 223 224 225
	lpcid = (CURSORICONDIR*)peimage;

	if( lpcid->idReserved || (lpcid->idType != 1) || (!lpcid->idCount) )
	  return 0;

	/* allocate the phony ICONDIR structure */
	*uSize = lpcid->idCount * sizeof(CURSORICONDIRENTRY) + HEADER_SIZE;
	if( (lpID = (CURSORICONDIR*)HeapAlloc(GetProcessHeap(),0, *uSize) ))
	{
	  /* copy the header */
	  lpID->idReserved = lpcid->idReserved;
	  lpID->idType = lpcid->idType;
	  lpID->idCount = lpcid->idCount;

226
	  /* copy the entries */
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
	  for( i=0; i < lpcid->idCount; i++ )
	  {
	    memcpy((void*)&(lpID->idEntries[i]),(void*)&(lpcid->idEntries[i]), sizeof(CURSORICONDIRENTRY) - 2);
	    lpID->idEntries[i].wResId = i;
	  }

	  *lplpiID = (LPicoICONDIR)peimage;
	  return (BYTE *)lpID;
	}
	return 0;
}

/*************************************************************************
 *	ICO_ExtractIconExW		[internal]
 *
 * NOTES
 *  nIcons = 0: returns number of Icons in file
 *
 * returns
246 247 248
 *  invalid file: -1
 *  failure:0;
 *  success: number of icons in file (nIcons = 0) or nr of icons retrieved
249
 */
250
static UINT ICO_ExtractIconExW(
251 252 253 254 255
	LPCWSTR lpszExeFileName,
	HICON * RetPtr,
	INT nIconIndex,
	UINT nIcons,
	UINT cxDesired,
256 257 258
	UINT cyDesired,
	UINT *pIconId,
	UINT flags)
259
{
260
	UINT		ret = 0;
261
	UINT		cx1, cx2, cy1, cy2;
262 263
	LPBYTE		pData;
	DWORD		sig;
264
	HANDLE		hFile;
265 266 267 268
	UINT16		iconDirCount = 0,iconCount = 0;
	LPBYTE		peimage;
	HANDLE		fmapping;
	ULONG		uSize;
269
	DWORD		fsizeh,fsizel;
270 271
        WCHAR		szExePath[MAX_PATH];
        DWORD		dwSearchReturn;
272

273
	TRACE("%s, %d, %d %p 0x%08x\n", debugstr_w(lpszExeFileName), nIconIndex, nIcons, pIconId, flags);
274

275 276 277 278 279 280 281 282
        dwSearchReturn = SearchPathW(NULL, lpszExeFileName, NULL, sizeof(szExePath) / sizeof(szExePath[0]), szExePath, NULL);
        if ((dwSearchReturn == 0) || (dwSearchReturn > sizeof(szExePath) / sizeof(szExePath[0])))
        {
            WARN("File %s not found or path too long\n", debugstr_w(lpszExeFileName));
            return -1;
        }

	hFile = CreateFileW(szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
283
	if (hFile == INVALID_HANDLE_VALUE) return ret;
284
	fsizel = GetFileSize(hFile,&fsizeh);
285 286

	/* Map the file */
287 288
	fmapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY | SEC_COMMIT, 0, 0, NULL);
	CloseHandle(hFile);
289 290 291
	if (!fmapping)
	{
	  WARN("CreateFileMapping error %ld\n", GetLastError() );
292
	  return 0xFFFFFFFF;
293 294
	}

295
	if (!(peimage = MapViewOfFile(fmapping, FILE_MAP_READ, 0, 0, 0)))
296 297
	{
	  WARN("MapViewOfFile error %ld\n", GetLastError() );
298 299
	  CloseHandle(fmapping);
	  return 0xFFFFFFFF;
300
	}
301
	CloseHandle(fmapping);
302 303 304 305 306 307 308 309 310 311 312 313

	cx1 = LOWORD(cxDesired);
	cx2 = HIWORD(cxDesired) ? HIWORD(cxDesired) : cx1;
	cy1 = LOWORD(cyDesired);
	cy2 = HIWORD(cyDesired) ? HIWORD(cyDesired) : cy1;

	if (pIconId) /* Invalidate first icon identifier */
		*pIconId = 0xFFFFFFFF;

	if (!pIconId) /* if no icon identifier array present use the icon handle array as intermediate storage */
	  pIconId = (UINT*)RetPtr;

314
	sig = USER32_GetResourceTable(peimage, fsizel, &pData);
315

316 317
/* ico file or NE exe/dll*/
	if (sig==IMAGE_OS2_SIGNATURE || sig==1) /* .ICO file */
318 319 320 321 322 323 324 325 326
	{
	  BYTE		*pCIDir = 0;
	  NE_TYPEINFO	*pTInfo = (NE_TYPEINFO*)(pData + 2);
	  NE_NAMEINFO	*pIconStorage = NULL;
	  NE_NAMEINFO	*pIconDir = NULL;
	  LPicoICONDIR	lpiID = NULL;

	  TRACE("-- OS2/icon Signature (0x%08lx)\n", sig);

327
	  if (pData == (BYTE*)-1)
328 329
	  {
	    pCIDir = ICO_GetIconDirectory(peimage, &lpiID, &uSize);	/* check for .ICO file */
330
	    if (pCIDir)
331 332 333 334 335
	    {
	      iconDirCount = 1; iconCount = lpiID->idCount;
	      TRACE("-- icon found %p 0x%08lx 0x%08x 0x%08x\n", pCIDir, uSize, iconDirCount, iconCount);
	    }
	  }
336
	  else while (pTInfo->type_id && !(pIconStorage && pIconDir))
337
	  {
338
	    if (pTInfo->type_id == NE_RSCTYPE_GROUP_ICON)	/* find icon directory and icon repository */
339 340 341 342 343
	    {
	      iconDirCount = pTInfo->count;
	      pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
	      TRACE("\tfound directory - %i icon families\n", iconDirCount);
	    }
344
	    if (pTInfo->type_id == NE_RSCTYPE_ICON)
345 346 347 348 349 350 351 352
	    {
	      iconCount = pTInfo->count;
	      pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
	      TRACE("\ttotal icons - %i\n", iconCount);
	    }
	    pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
	  }

353
	  if ((pIconStorage && pIconDir) || lpiID)	  /* load resources and create icons */
354
	  {
355
	    if (nIcons == 0)
356
	    {
357
	      ret = iconDirCount;
358 359
	      if (lpiID && pCIDir)	/* *.ico file, deallocate heap pointer*/
	        HeapFree(GetProcessHeap(), 0, pCIDir);
360
	    }
361
	    else if (nIconIndex < iconDirCount)
362 363
	    {
	      UINT16   i, icon;
364
	      if (nIcons > iconDirCount - nIconIndex)
365 366
	        nIcons = iconDirCount - nIconIndex;

367
	      for (i = 0; i < nIcons; i++)
368 369
	      {
	        /* .ICO files have only one icon directory */
370 371
	        if (lpiID == NULL)	/* not *.ico */
	          pCIDir = USER32_LoadResource(peimage, pIconDir + i + nIconIndex, *(WORD*)pData, &uSize);
372
	        pIconId[i] = LookupIconIdFromDirectoryEx(pCIDir, TRUE, (i & 1) ? cx2 : cx1, (i & 1) ? cy2 : cy1, flags);
373
	      }
374 375
	      if (lpiID && pCIDir)	/* *.ico file, deallocate heap pointer*/
	        HeapFree(GetProcessHeap(), 0, pCIDir);
376

377
	      for (icon = 0; icon < nIcons; icon++)
378 379
	      {
	        pCIDir = NULL;
380
	        if (lpiID)
381
	          pCIDir = ICO_LoadIcon(peimage, lpiID->idEntries + (int)pIconId[icon], &uSize);
382
	        else
383 384 385
	          for (i = 0; i < iconCount; i++)
	            if (pIconStorage[i].id == ((int)pIconId[icon] | 0x8000) )
	              pCIDir = USER32_LoadResource(peimage, pIconStorage + i, *(WORD*)pData, &uSize);
386

387
	        if (pCIDir)
388 389
	          RetPtr[icon] = (HICON)CreateIconFromResourceEx(pCIDir, uSize, TRUE, 0x00030000,
	                                                         (icon & 1) ? cx2 : cx1, (icon & 1) ? cy2 : cy1, flags);
390
	        else
391
	          RetPtr[icon] = 0;
392
	      }
393
	      ret = icon;	/* return number of retrieved icons */
394 395
	    }
	  }
396
	}
397 398 399 400 401 402 403 404 405
/* end ico file */

/* exe/dll */
	else if( sig == IMAGE_NT_SIGNATURE )
	{
	  LPBYTE		idata,igdata;
	  PIMAGE_DOS_HEADER	dheader;
	  PIMAGE_NT_HEADERS	pe_header;
	  PIMAGE_SECTION_HEADER	pe_sections;
406 407 408
	  const IMAGE_RESOURCE_DIRECTORY *rootresdir,*iconresdir,*icongroupresdir;
	  const IMAGE_RESOURCE_DATA_ENTRY *idataent,*igdataent;
	  const IMAGE_RESOURCE_DIRECTORY_ENTRY *xresent;
409
	  UINT	i, j;
410

411 412
	  dheader = (PIMAGE_DOS_HEADER)peimage;
	  pe_header = (PIMAGE_NT_HEADERS)(peimage+dheader->e_lfanew);	  /* it is a pe header, USER32_GetResourceTable checked that */
413 414
	  pe_sections = (PIMAGE_SECTION_HEADER)(((char*)pe_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)
	                                        + pe_header->FileHeader.SizeOfOptionalHeader);
415 416 417
	  rootresdir = NULL;

	  /* search for the root resource directory */
418
	  for (i=0;i<pe_header->FileHeader.NumberOfSections;i++)
419 420 421
	  {
	    if (pe_sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
	      continue;
422 423 424 425 426 427 428 429
	    if (fsizel < pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData) {
	      FIXME("File %s too short (section is at %ld bytes, real size is %ld)\n",
		      debugstr_w(lpszExeFileName),
		      pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData,
		      fsizel
	      );
	      goto end;
	    }
430
	    /* FIXME: doesn't work when the resources are not in a separate section */
431
	    if (pe_sections[i].VirtualAddress == pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress)
432 433 434 435 436 437
	    {
	      rootresdir = (PIMAGE_RESOURCE_DIRECTORY)(peimage+pe_sections[i].PointerToRawData);
	      break;
	    }
	  }

438
	  if (!rootresdir)
439 440
	  {
	    WARN("haven't found section for resource directory.\n");
441
	    goto end;		/* failure */
442 443 444
	  }

	  /* search for the group icon directory */
445
	  if (!(icongroupresdir = find_entry_by_id(rootresdir, LOWORD(RT_GROUP_ICON), rootresdir)))
446 447
	  {
	    WARN("No Icongroupresourcedirectory!\n");
448
	    goto end;		/* failure */
449 450 451 452 453 454
	  }
	  iconDirCount = icongroupresdir->NumberOfNamedEntries + icongroupresdir->NumberOfIdEntries;

	  /* only number of icons requested */
	  if( nIcons == 0 )
	  {
455
	    ret = iconDirCount;
456
	    goto end;		/* success */
457 458 459 460 461 462 463 464 465 466
	  }

	  if( nIconIndex < 0 )
	  {
	    /* search resource id */
	    int n = 0;
	    int iId = abs(nIconIndex);
	    PIMAGE_RESOURCE_DIRECTORY_ENTRY xprdeTmp = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(icongroupresdir+1);

	    while(n<iconDirCount && xprdeTmp)
467
	    {
468
              if(xprdeTmp->u1.s2.Id ==  iId)
469 470 471 472 473
              {
                  nIconIndex = n;
                  break;
              }
              n++;
474
              xprdeTmp++;
475 476 477 478
	    }
	    if (nIconIndex < 0)
	    {
	      WARN("resource id %d not found\n", iId);
479
	      goto end;		/* failure */
480 481 482 483 484
	    }
	  }
	  else
	  {
	    /* check nIconIndex to be in range */
485
	    if (nIconIndex >= iconDirCount)
486 487
	    {
	      WARN("nIconIndex %d is larger than iconDirCount %d\n",nIconIndex,iconDirCount);
488
	      goto end;		/* failure */
489 490 491 492 493 494 495 496 497 498
	    }
	  }

	  /* assure we don't get too much */
	  if( nIcons > iconDirCount - nIconIndex )
	    nIcons = iconDirCount - nIconIndex;

	  /* starting from specified index */
	  xresent = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(icongroupresdir+1) + nIconIndex;

499
	  for (i=0; i < nIcons; i++,xresent++)
500
	  {
501
	    const IMAGE_RESOURCE_DIRECTORY *resdir;
502 503

	    /* go down this resource entry, name */
504
	    resdir = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)rootresdir+(xresent->u2.s3.OffsetToDirectory));
505 506

	    /* default language (0) */
507
	    resdir = find_entry_default(resdir,rootresdir);
508 509 510 511 512
	    igdataent = (PIMAGE_RESOURCE_DATA_ENTRY)resdir;

	    /* lookup address in mapped image for virtual address */
	    igdata = NULL;

513
	    for (j=0;j<pe_header->FileHeader.NumberOfSections;j++)
514 515 516 517
	    {
	      if (igdataent->OffsetToData < pe_sections[j].VirtualAddress)
	        continue;
	      if (igdataent->OffsetToData+igdataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
518
	        continue;
519 520

	      if (igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData+igdataent->Size > fsizel) {
521 522 523
	        FIXME("overflow in PE lookup (%s has len %ld, have offset %ld), short file?\n", debugstr_w(lpszExeFileName), fsizel,
	        	   igdataent->OffsetToData - pe_sections[j].VirtualAddress + pe_sections[j].PointerToRawData + igdataent->Size);
	        goto end; /* failure */
524
	      }
525 526 527
	      igdata = peimage+(igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
	    }

528
	    if (!igdata)
529
	    {
530
	      FIXME("no matching real address for icongroup!\n");
531
	      goto end;	/* failure */
532
	    }
533
	    pIconId[i] = LookupIconIdFromDirectoryEx(igdata, TRUE, (i & 1) ? cx2 : cx1, (i & 1) ? cy2 : cy1, flags);
534 535
	  }

536
	  if (!(iconresdir=find_entry_by_id(rootresdir,LOWORD(RT_ICON),rootresdir)))
537 538
	  {
	    WARN("No Iconresourcedirectory!\n");
539
	    goto end;		/* failure */
540 541
	  }

542
	  for (i=0; i<nIcons; i++)
543
	  {
544 545 546
	    const IMAGE_RESOURCE_DIRECTORY *xresdir;
	    xresdir = find_entry_by_id(iconresdir, LOWORD(pIconId[i]), rootresdir);
	    xresdir = find_entry_default(xresdir, rootresdir);
547 548 549 550
	    idataent = (PIMAGE_RESOURCE_DATA_ENTRY)xresdir;
	    idata = NULL;

	    /* map virtual to address in image */
551
	    for (j=0;j<pe_header->FileHeader.NumberOfSections;j++)
552 553 554 555 556 557 558
	    {
	      if (idataent->OffsetToData < pe_sections[j].VirtualAddress)
	        continue;
	      if (idataent->OffsetToData+idataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
	        continue;
	      idata = peimage+(idataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
	    }
559
	    if (!idata)
560 561 562 563 564
	    {
	      WARN("no matching real address found for icondata!\n");
	      RetPtr[i]=0;
	      continue;
	    }
565 566
	    RetPtr[i] = (HICON) CreateIconFromResourceEx(idata,idataent->Size,TRUE,0x00030000,
	                                                 (i & 1) ? cx2 : cx1, (i & 1) ? cy2 : cy1, flags);
567
	  }
568
	  ret = i;	/* return number of retrieved icons */
569 570
	}			/* if(sig == IMAGE_NT_SIGNATURE) */

571 572 573
end:
	UnmapViewOfFile(peimage);	/* success */
	return ret;
574 575 576 577 578 579
}

/***********************************************************************
 *           PrivateExtractIconsW			[USER32.@]
 *
 * NOTES
580 581 582 583 584 585
 *  If HIWORD(sizeX) && HIWORD(sizeY) 2 * ((nIcons + 1) MOD 2) icons are
 *  returned, with the LOWORD size icon first and the HIWORD size icon
 *  second.
 *  Also the Windows equivalent does extract icons in a strange way if
 *  nIndex is negative. Our implementation treats a negative nIndex as
 *  looking for that resource identifier for the first icon to retrieve.
586
 *
587
 * FIXME:
588 589
 *  should also support 16 bit EXE + DLLs, cursor and animated cursor as
 *  well as bitmap files.
590 591
 */

592
UINT WINAPI PrivateExtractIconsW (
593 594
	LPCWSTR lpwstrFile,
	int nIndex,
595 596
	int sizeX,
	int sizeY,
597 598
	HICON * phicon, /* [out] pointer to array of nIcons HICON handles */
	UINT* pIconId,  /* [out] pointer to array of nIcons icon identifiers or NULL */
599
	UINT nIcons,    /* [in] number of icons to retrieve */
600
	UINT flags )    /* [in] LR_* flags used by LoadImage */
601
{
602 603
	TRACE("%s %d %dx%d %p %p %d 0x%08x\n",
	      debugstr_w(lpwstrFile), nIndex, sizeX, sizeY, phicon, pIconId, nIcons, flags);
604

605 606
	if ((nIcons & 1) && HIWORD(sizeX) && HIWORD(sizeY))
	{
607
	  WARN("Uneven number %d of icons requested for small and large icons!\n", nIcons);
608 609
	}
	return ICO_ExtractIconExW(lpwstrFile, phicon, nIndex, nIcons, sizeX, sizeY, pIconId, flags);
610 611 612 613 614 615
}

/***********************************************************************
 *           PrivateExtractIconsA			[USER32.@]
 */

616
UINT WINAPI PrivateExtractIconsA (
617
	LPCSTR lpstrFile,
618 619 620
	int nIndex,
	int sizeX,
	int sizeY,
621 622
	HICON * phicon, /* [out] pointer to array of nIcons HICON handles */
	UINT* piconid,  /* [out] pointer to array of nIcons icon identifiers or NULL */
623 624
	UINT nIcons,    /* [in] number of icons to retrieve */
	UINT flags )    /* [in] LR_* flags used by LoadImage */
625
{
626 627 628
    UINT ret;
    INT len = MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, NULL, 0);
    LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
629

630 631
    MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, lpwstrFile, len);
    ret = PrivateExtractIconsW(lpwstrFile, nIndex, sizeX, sizeY, phicon, piconid, nIcons, flags);
632

633 634
    HeapFree(GetProcessHeap(), 0, lpwstrFile);
    return ret;
635 636 637
}

/***********************************************************************
638
 *           PrivateExtractIconExW			[USER32.@]
639
 * NOTES
640
 *  if nIndex == -1 it returns the number of icons in any case !!!
641
 */
642
UINT WINAPI PrivateExtractIconExW (
643
	LPCWSTR lpwstrFile,
644
	int nIndex,
645 646 647 648 649
	HICON * phIconLarge,
	HICON * phIconSmall,
	UINT nIcons )
{
	DWORD cyicon, cysmicon, cxicon, cxsmicon;
650
	UINT ret = 0;
651

652
	TRACE("%s %d %p %p %d\n",
653
	debugstr_w(lpwstrFile),nIndex,phIconLarge, phIconSmall, nIcons);
654

655 656 657 658 659
	if (nIndex == -1)
	  /* get the number of icons */
	  return ICO_ExtractIconExW(lpwstrFile, NULL, 0, 0, 0, 0, NULL, LR_DEFAULTCOLOR);

	if (nIcons == 1 && phIconSmall && phIconLarge)
660 661 662 663 664 665
	{
	  HICON hIcon[2];
	  cxicon = GetSystemMetrics(SM_CXICON);
	  cyicon = GetSystemMetrics(SM_CYICON);
	  cxsmicon = GetSystemMetrics(SM_CXSMICON);
	  cysmicon = GetSystemMetrics(SM_CYSMICON);
666

667 668
	  ret = ICO_ExtractIconExW(lpwstrFile, (HICON*) &hIcon, nIndex, 2, cxicon | (cxsmicon<<16),
	                           cyicon | (cysmicon<<16), NULL, LR_DEFAULTCOLOR);
669 670 671 672 673
	  *phIconLarge = hIcon[0];
	  *phIconSmall = hIcon[1];
 	  return ret;
	}

674
	if (phIconSmall)
675
	{
676 677 678 679 680
	  /* extract n small icons */
	  cxsmicon = GetSystemMetrics(SM_CXSMICON);
	  cysmicon = GetSystemMetrics(SM_CYSMICON);
	  ret = ICO_ExtractIconExW(lpwstrFile, phIconSmall, nIndex, nIcons, cxsmicon,
	                           cysmicon, NULL, LR_DEFAULTCOLOR);
681
	}
682
       if (phIconLarge)
683 684 685 686
	{
	  /* extract n large icons */
	  cxicon = GetSystemMetrics(SM_CXICON);
	  cyicon = GetSystemMetrics(SM_CYICON);
687
         ret = ICO_ExtractIconExW(lpwstrFile, phIconLarge, nIndex, nIcons, cxicon,
688 689 690
	                           cyicon, NULL, LR_DEFAULTCOLOR);
	}
	return ret;
691 692 693
}

/***********************************************************************
694
 *           PrivateExtractIconExA			[USER32.@]
695
 */
696
UINT WINAPI PrivateExtractIconExA (
697
	LPCSTR lpstrFile,
698
	int nIndex,
699 700 701 702
	HICON * phIconLarge,
	HICON * phIconSmall,
	UINT nIcons )
{
703 704 705
	UINT ret;
	INT len = MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, NULL, 0);
	LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
706

707
	TRACE("%s %d %p %p %d\n", lpstrFile, nIndex, phIconLarge, phIconSmall, nIcons);
708

709 710
	MultiByteToWideChar(CP_ACP, 0, lpstrFile, -1, lpwstrFile, len);
	ret = PrivateExtractIconExW(lpwstrFile, nIndex, phIconLarge, phIconSmall, nIcons);
711 712 713
	HeapFree(GetProcessHeap(), 0, lpwstrFile);
	return ret;
}