You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1021 lines
29 KiB
1021 lines
29 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: acons.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains code for dealing with animated icons/cursors.
|
|
*
|
|
* History:
|
|
* 10-02-91 DarrinM Created.
|
|
* 07-30-92 DarrinM Unicodized.
|
|
* 11-28-94 JimA Moved to client from server.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/*
|
|
* Resource Directory format for IconEditor generated icon and cursor
|
|
* (.ICO & .CUR) files. All fields are shared except xHotspot and yHotspot
|
|
* which are only valid for cursors.
|
|
*/
|
|
typedef struct _ICONFILERESDIR { // ird
|
|
BYTE bWidth;
|
|
BYTE bHeight;
|
|
BYTE bColorCount;
|
|
BYTE bReserved;
|
|
WORD xHotspot;
|
|
WORD yHotspot;
|
|
DWORD dwDIBSize;
|
|
DWORD dwDIBOffset;
|
|
} ICONFILERESDIR;
|
|
|
|
typedef struct _HOTSPOTREC { // hs
|
|
WORD xHotspot;
|
|
WORD yHotspot;
|
|
} HOTSPOTREC;
|
|
|
|
PCURSORRESOURCE ReadIconGuts(
|
|
IN PFILEINFO pfi,
|
|
IN LPNEWHEADER pnhBase,
|
|
IN int offResBase,
|
|
OUT LPWSTR *prt,
|
|
IN int cxDesired,
|
|
IN int cyDesired,
|
|
IN DWORD LR_flags);
|
|
|
|
BOOL ReadTag(
|
|
IN PFILEINFO pfi,
|
|
OUT PRTAG ptag);
|
|
|
|
BOOL ReadChunk(
|
|
IN PFILEINFO pfi,
|
|
IN PRTAG ptag,
|
|
OUT PVOID pv);
|
|
|
|
BOOL SkipChunk(
|
|
IN PFILEINFO pfi,
|
|
IN PRTAG ptag);
|
|
|
|
HICON CreateAniIcon(
|
|
LPCWSTR pszName,
|
|
LPWSTR rt,
|
|
int cicur,
|
|
DWORD *aicur,
|
|
int cpcur,
|
|
HCURSOR *ahcur,
|
|
JIF jifRate,
|
|
PJIF ajifRate,
|
|
BOOL fPublic);
|
|
|
|
HCURSOR ReadIconFromFileMap(
|
|
IN PFILEINFO pfi,
|
|
IN int cbSize,
|
|
IN DWORD cxDesired,
|
|
IN DWORD cyDesired,
|
|
IN DWORD LR_flags);
|
|
|
|
HICON LoadAniIcon(
|
|
IN PFILEINFO pfi,
|
|
IN LPWSTR rt,
|
|
IN DWORD cxDesired,
|
|
IN DWORD cyDesired,
|
|
IN DWORD LR_flags);
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* LoadCursorFromFile (API)
|
|
*
|
|
* Called by SetSystemCursor.
|
|
*
|
|
* History:
|
|
* 08-03-92 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG1(LOG_GENERAL, HCURSOR, WINAPI, LoadCursorFromFileW, LPCWSTR, pszFilename)
|
|
HCURSOR WINAPI LoadCursorFromFileW(
|
|
LPCWSTR pszFilename)
|
|
{
|
|
return(LoadImage(NULL,
|
|
pszFilename,
|
|
IMAGE_CURSOR,
|
|
0,
|
|
0,
|
|
LR_DEFAULTSIZE | LR_LOADFROMFILE));
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* LoadCursorFromFileA
|
|
*
|
|
* Returns: hCursor
|
|
*
|
|
* 10/9/1995 Created SanfordS
|
|
\***********************************************************************/
|
|
|
|
|
|
FUNCLOG1(LOG_GENERAL, HCURSOR, WINAPI, LoadCursorFromFileA, LPCSTR, pszFilename)
|
|
HCURSOR WINAPI LoadCursorFromFileA(
|
|
LPCSTR pszFilename)
|
|
{
|
|
LPWSTR lpUniName;
|
|
HCURSOR hcur;
|
|
|
|
if (pszFilename == NULL ||
|
|
!MBToWCS(pszFilename, -1, &lpUniName, -1, TRUE))
|
|
return (HANDLE)NULL;
|
|
|
|
hcur = LoadCursorFromFileW(lpUniName);
|
|
|
|
UserLocalFree(lpUniName);
|
|
|
|
return hcur;
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* ReadFilePtr
|
|
*
|
|
* Works like ReadFile but with pointers to a mapped file buffer.
|
|
*
|
|
* Returns:
|
|
*
|
|
* 11/16/1995 Created SanfordS
|
|
\***********************************************************************/
|
|
BOOL ReadFilePtr(
|
|
IN PFILEINFO pfi,
|
|
OUT LPVOID *ppBuf,
|
|
IN DWORD cb)
|
|
{
|
|
*ppBuf = pfi->pFilePtr;
|
|
pfi->pFilePtr += cb;
|
|
return (pfi->pFilePtr <= pfi->pFileEnd);
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* ReadFilePtrUnaligned
|
|
*
|
|
* Works like ReadFile but with pointers to a mapped file buffer.
|
|
*
|
|
* Returns:
|
|
*
|
|
* 11/16/1995 Created SanfordS
|
|
\***********************************************************************/
|
|
BOOL ReadFilePtrUnaligned(
|
|
IN PFILEINFO pfi,
|
|
OUT VOID UNALIGNED **ppBuf,
|
|
IN DWORD cb)
|
|
{
|
|
*ppBuf = pfi->pFilePtr;
|
|
pfi->pFilePtr += cb;
|
|
return (pfi->pFilePtr <= pfi->pFileEnd);
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* ReadFilePtrCopy
|
|
*
|
|
* Works even more like ReadFile in that is copies data to the given buffer.
|
|
*
|
|
* Returns:
|
|
*
|
|
* 11/16/1995 Created SanfordS
|
|
\***********************************************************************/
|
|
BOOL ReadFilePtrCopy(
|
|
IN PFILEINFO pfi,
|
|
IN OUT LPVOID pBuf,
|
|
IN DWORD cb)
|
|
{
|
|
if (pfi->pFilePtr + cb > pfi->pFileEnd) {
|
|
return(FALSE);
|
|
}
|
|
RtlCopyMemory(pBuf, pfi->pFilePtr, cb);
|
|
pfi->pFilePtr += cb;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* ReadTag, ReadChunk, SkipChunk
|
|
*
|
|
* Some handy functions for reading RIFF files.
|
|
*
|
|
* History:
|
|
* 10-02-91 DarrinM Created.
|
|
* 03-25-93 Jonpa Changed to use RIFF format instead of ASDF
|
|
\***************************************************************************/
|
|
BOOL ReadTag(
|
|
IN PFILEINFO pfi,
|
|
OUT PRTAG ptag)
|
|
{
|
|
ptag->ckID = ptag->ckSize = 0L; // in case we fail the read.
|
|
|
|
return(ReadFilePtrCopy(pfi, ptag, sizeof(RTAG)));
|
|
}
|
|
|
|
|
|
|
|
BOOL ReadChunk(
|
|
IN PFILEINFO pfi,
|
|
IN PRTAG ptag,
|
|
OUT PVOID pv)
|
|
{
|
|
if (!ReadFilePtrCopy(pfi, pv, ptag->ckSize))
|
|
return FALSE;
|
|
|
|
/* WORD align file pointer */
|
|
if( ptag->ckSize & 1 )
|
|
pfi->pFilePtr++;
|
|
|
|
|
|
if (pfi->pFilePtr <= pfi->pFileEnd) {
|
|
return TRUE;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "ReadChunk: Advanced pointer past end of file map");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL SkipChunk(
|
|
IN PFILEINFO pfi,
|
|
IN PRTAG ptag)
|
|
{
|
|
/*
|
|
* Round ptag->ckSize up to nearest word boundary
|
|
* to maintain alignment
|
|
*/
|
|
pfi->pFilePtr += (ptag->ckSize + 1) & (~1);
|
|
if (pfi->pFilePtr <= pfi->pFileEnd) {
|
|
return TRUE;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "SkipChunk: Advanced pointer past end of file map");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* LoadCursorIconFromFileMap
|
|
*
|
|
* If pszName is one of the IDC_* values then we use WIN.INI to find a
|
|
* custom cursor/icon. Otherwise, pszName points to a filename of a .ICO/.CUR
|
|
* file to be loaded. If the file is an .ANI file containing a multiframe
|
|
* animation then LoadAniIcon is called to create an ACON. Otherwise if
|
|
* the file is an .ANI file containing just a single frame then it is loaded
|
|
* and a normal CURSOR/ICON resource is created from it.
|
|
*
|
|
* 12-26-91 DarrinM Wrote it.
|
|
* 03-17-93 JonPa Changed to use RIFF format for ani-cursors
|
|
* 11/16/1995 SanfordS Added LR_flags support
|
|
\***************************************************************************/
|
|
|
|
HANDLE LoadCursorIconFromFileMap(
|
|
IN PFILEINFO pfi,
|
|
IN OUT LPWSTR *prt,
|
|
IN DWORD cxDesired,
|
|
IN DWORD cyDesired,
|
|
IN DWORD LR_flags,
|
|
OUT LPBOOL pfAni)
|
|
{
|
|
LPNEWHEADER pnh;
|
|
int offResBase;
|
|
|
|
*pfAni = FALSE;
|
|
offResBase = 0;
|
|
|
|
/*
|
|
* Determine if this is an .ICO/.CUR file or an .ANI file.
|
|
*/
|
|
pnh = (LPNEWHEADER)pfi->pFileMap;
|
|
if (*(LPDWORD)pnh == FOURCC_RIFF) {
|
|
|
|
RTAG tag;
|
|
|
|
/*
|
|
* It's an ANICURSOR!
|
|
* Seek back to beginning + 1 tag.
|
|
*/
|
|
pfi->pFilePtr = pfi->pFileMap + sizeof(tag);
|
|
|
|
/* check RIFF type for ACON */
|
|
if (*(LPDWORD)pfi->pFilePtr != FOURCC_ACON) {
|
|
return NULL;
|
|
}
|
|
pfi->pFilePtr += sizeof(DWORD);
|
|
if (pfi->pFilePtr > pfi->pFileEnd) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Ok, we have a ACON chunk. Find the first ICON chunk and set
|
|
* things up so it looks we've just loaded the header of a normal
|
|
* .CUR file, then fall into the .CUR bits handling code below.
|
|
*/
|
|
while (ReadTag(pfi, &tag)) {
|
|
/*
|
|
* Handle each chunk type.
|
|
*/
|
|
if (tag.ckID == FOURCC_anih) {
|
|
|
|
ANIHEADER anih;
|
|
|
|
if (!ReadChunk(pfi, &tag, &anih)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(anih.fl & AF_ICON) || (anih.cFrames == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
// If this ACON has more than one frame then go ahead
|
|
// and create an ACON, otherwise just use the first
|
|
// frame to create a normal ICON/CURSOR.
|
|
|
|
if (anih.cFrames > 1) {
|
|
|
|
*pfAni = TRUE;
|
|
*prt = RT_CURSOR;
|
|
return(LoadAniIcon(pfi,
|
|
RT_CURSOR,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags));
|
|
}
|
|
|
|
} else if (tag.ckID == FOURCC_LIST) {
|
|
LPDWORD pdwType = NULL;
|
|
BOOL fOK = FALSE;
|
|
/*
|
|
* If this is the fram list, then get the first icon out of it
|
|
*/
|
|
|
|
/* check LIST type for fram */
|
|
|
|
if( tag.ckSize >= sizeof(DWORD) &&
|
|
(fOK = ReadFilePtr( pfi,
|
|
&pdwType,
|
|
sizeof(DWORD))) &&
|
|
*pdwType == FOURCC_fram) {
|
|
|
|
if (!ReadTag(pfi, &tag)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (tag.ckID == FOURCC_icon) {
|
|
/*
|
|
* We've found what we're looking for. Get current position
|
|
* in file to be used as the base from which the icon data
|
|
* offsets are offset from.
|
|
*/
|
|
offResBase = (int)(pfi->pFilePtr - pfi->pFileMap);
|
|
|
|
/*
|
|
* Grab the header first, since the following code assumes
|
|
* it was read above.
|
|
*/
|
|
ReadFilePtr(pfi, &pnh, sizeof(NEWHEADER));
|
|
|
|
/*
|
|
* Break out and let the icon loading/cursor creating code
|
|
* take it from here.
|
|
*/
|
|
break;
|
|
} else {
|
|
SkipChunk(pfi, &tag);
|
|
}
|
|
} else {
|
|
/*
|
|
* Something bad happened in the type read, if it was
|
|
* a file error then close and exit, otherwise just
|
|
* skip the rest of the chunk
|
|
*/
|
|
if(!fOK) {
|
|
return NULL;
|
|
}
|
|
/*
|
|
* take the type we just read out of the tag size and
|
|
* skip the rest
|
|
*/
|
|
tag.ckSize -= sizeof(DWORD);
|
|
SkipChunk(pfi, &tag);
|
|
}
|
|
} else {
|
|
/*
|
|
* We're not interested in this chunk, skip it.
|
|
*/
|
|
SkipChunk(pfi, &tag);
|
|
}
|
|
}
|
|
} else { // not a RIFF file.
|
|
if ((pnh->ResType != FT_ICON) && (pnh->ResType != FT_CURSOR)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
{
|
|
PCURSORRESOURCE pcres;
|
|
|
|
pcres = ReadIconGuts(pfi,
|
|
pnh,
|
|
offResBase,
|
|
prt,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags);
|
|
|
|
if (pcres == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return ConvertDIBIcon((LPBITMAPINFOHEADER)pcres,
|
|
NULL,
|
|
pfi->pszName,
|
|
*prt == RT_ICON,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************\
|
|
* ReadIconGuts
|
|
*
|
|
* Returns: a pointer to a locally allocated buffer extraced from the
|
|
* given file that looks like a icon/acon resource.
|
|
* Also returns the type of the icon (RT_ICON or RT_CURSOR)
|
|
*
|
|
*
|
|
* 8/23/1995 SanfordS Documented
|
|
* 11/16/1995 SanfordS Added LR_flags support
|
|
\***********************************************************************/
|
|
PCURSORRESOURCE ReadIconGuts(
|
|
IN PFILEINFO pfi,
|
|
IN NEWHEADER *pnhBase,
|
|
IN int offResBase,
|
|
OUT LPWSTR *prt,
|
|
IN int cxDesired,
|
|
IN int cyDesired,
|
|
IN DWORD LR_flags)
|
|
{
|
|
NEWHEADER *pnh;
|
|
int i, Id;
|
|
ICONFILERESDIR UNALIGNED *pird;
|
|
PCURSORRESOURCE pcres;
|
|
RESDIR UNALIGNED *prd;
|
|
DWORD cb;
|
|
HOTSPOTREC UNALIGNED *phs;
|
|
LPBITMAPINFOHEADER pbih;
|
|
|
|
/*
|
|
* Construct a fake array of RESDIR entries using the info at the head
|
|
* of the file. Store the data offset in the idIcon WORD so it can be
|
|
* returned by RtlGetIdFromDirectory.
|
|
*/
|
|
pnh = (NEWHEADER *)UserLocalAlloc(0, sizeof(NEWHEADER) +
|
|
(pnhBase->ResCount * (sizeof(RESDIR) + sizeof(HOTSPOTREC))));
|
|
if (pnh == NULL)
|
|
return NULL;
|
|
|
|
*pnh = *pnhBase;
|
|
prd = (RESDIR UNALIGNED *)(pnh + 1);
|
|
phs = (HOTSPOTREC UNALIGNED *)(prd + pnhBase->ResCount);
|
|
|
|
for (i = 0; i < (int)pnh->ResCount; i++, prd++) {
|
|
/*
|
|
* Read the resource directory from the icon file.
|
|
*/
|
|
ReadFilePtrUnaligned(pfi, &pird, sizeof(ICONFILERESDIR));
|
|
|
|
/*
|
|
* Convert from the icon editor's resource directory format
|
|
* to the post-RC.EXE format LookupIconIdFromDirectory expects.
|
|
*/
|
|
prd->Icon.Width = pird->bWidth;
|
|
prd->Icon.Height = pird->bHeight;
|
|
prd->Icon.reserved = 0;
|
|
prd->BytesInRes = pird->dwDIBSize;
|
|
prd->idIcon = (WORD)pird->dwDIBOffset;
|
|
|
|
if (pnh->ResType == FT_ICON) {
|
|
/*
|
|
* 10/18/2000 - dwaynen
|
|
*
|
|
* For icons, this is really an ICONDIRENTRY (which has
|
|
* wPlanes and wBitCount fields that overlap xHotSpot and
|
|
* yHotSpot!
|
|
*/
|
|
prd->Icon.ColorCount = pird->bColorCount;
|
|
prd->Planes = pird->xHotspot;
|
|
prd->BitCount = pird->yHotspot;
|
|
} else {
|
|
/*
|
|
* 10/18/2000 - dwaynen
|
|
*
|
|
* Hopefully, cursors will only have one image. Otherwise,
|
|
* our selection logic is gonna be screwed up because we don't
|
|
* store the color bit depth! I suppose we could dig out the
|
|
* actual bitmap header and find the info there. Consider doing
|
|
* this if we ever want to support multi-resource cursors.
|
|
*/
|
|
prd->Icon.ColorCount = 0;
|
|
prd->Planes = 0;
|
|
prd->BitCount = 0;
|
|
}
|
|
|
|
phs->xHotspot = pird->xHotspot;
|
|
phs->yHotspot = pird->yHotspot;
|
|
phs++;
|
|
}
|
|
|
|
*prt = pnhBase->ResType == FT_ICON ? RT_ICON : RT_CURSOR;
|
|
Id = RtlGetIdFromDirectory((PBYTE)pnh,
|
|
*prt == RT_ICON,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags,
|
|
&cb);
|
|
|
|
/*
|
|
* Allocate for worst case (cursor).
|
|
*/
|
|
pcres = (PCURSORRESOURCE)UserLocalAlloc(0,
|
|
cb + FIELD_OFFSET(CURSORRESOURCE, bih));
|
|
if (pcres == NULL) {
|
|
goto CleanExit;
|
|
}
|
|
|
|
if (*prt == RT_CURSOR) {
|
|
/*
|
|
* Fill in hotspot info for cursors.
|
|
*/
|
|
prd = (RESDIR UNALIGNED *)(pnh + 1);
|
|
phs = (HOTSPOTREC UNALIGNED *)(prd + pnh->ResCount);
|
|
|
|
for( i = 0; i < pnh->ResCount; i++ ) {
|
|
if (prd[i].idIcon == (WORD)Id) {
|
|
pcres->xHotspot = phs[i].xHotspot;
|
|
pcres->yHotspot = phs[i].yHotspot;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == pnh->ResCount) {
|
|
pcres->xHotspot = pird->xHotspot;
|
|
pcres->yHotspot = pird->yHotspot;
|
|
}
|
|
pbih = &pcres->bih;
|
|
} else {
|
|
pbih = (LPBITMAPINFOHEADER)pcres;
|
|
}
|
|
|
|
/*
|
|
* Read in the header information into pcres.
|
|
*/
|
|
pfi->pFilePtr = pfi->pFileMap + offResBase + Id;
|
|
if (!ReadFilePtrCopy(pfi, pbih, cb)) {
|
|
UserLocalFree(pnh);
|
|
UserLocalFree(pcres);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
CleanExit:
|
|
UserLocalFree(pnh);
|
|
return pcres;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* CreateAniIcon
|
|
*
|
|
* For now, CreateAniIcon copies the jif rate table and the sequence table
|
|
* but not the CURSOR structs. This is ok as long as this routine is
|
|
* internal only.
|
|
*
|
|
* History:
|
|
* 10-02-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
HCURSOR CreateAniIcon(
|
|
LPCWSTR pszName,
|
|
LPWSTR rt,
|
|
int cicur,
|
|
DWORD *aicur,
|
|
int cpcur,
|
|
HCURSOR *ahcur,
|
|
JIF jifRate,
|
|
PJIF ajifRate,
|
|
BOOL fPublic)
|
|
{
|
|
HCURSOR hacon;
|
|
CURSORDATA acon;
|
|
DWORD cbacon;
|
|
HCURSOR *ahcurT; // Array of image frame pointers
|
|
DWORD *aicurT; // Array of frame indices (sequence table)
|
|
PJIF ajifRateT; // Array of time offsets
|
|
int i;
|
|
|
|
/*
|
|
* Start by allocating space for the ACON structure and the ahcur and
|
|
* ajifRate arrays.
|
|
*/
|
|
hacon = (HCURSOR)NtUserCallOneParam(fPublic,
|
|
SFI__CREATEEMPTYCURSOROBJECT);
|
|
if (hacon == NULL)
|
|
return NULL;
|
|
|
|
/*
|
|
* Save a couple UserLocalAlloc calls by allocating the memory needed for
|
|
* the CURSOR, JIF, and SEQ arrays at once.
|
|
*/
|
|
RtlZeroMemory(&acon, sizeof(acon));
|
|
cbacon = (cpcur * sizeof(HCURSOR)) +
|
|
(cicur * sizeof(JIF)) + (cicur * sizeof(DWORD));
|
|
ahcurT = (HCURSOR *)UserLocalAlloc(HEAP_ZERO_MEMORY, cbacon);
|
|
if (ahcurT == NULL) {
|
|
NtUserDestroyCursor((HCURSOR)hacon, CURSOR_ALWAYSDESTROY);
|
|
return NULL;
|
|
}
|
|
acon.aspcur = (PCURSOR *)ahcurT;
|
|
|
|
/*
|
|
* Set up work pointers
|
|
*/
|
|
ajifRateT = (PJIF)((PBYTE)ahcurT + (cpcur * sizeof(HCURSOR)));
|
|
aicurT = (DWORD *)((PBYTE)ajifRateT + (cicur * sizeof(JIF)));
|
|
|
|
/*
|
|
* Save offsets to arrays to make copying them to the server
|
|
* easier.
|
|
*/
|
|
acon.ajifRate = (PJIF)(cpcur * sizeof(HCURSOR));
|
|
acon.aicur = (KPDWORD)((KPBYTE)acon.ajifRate + (cicur * sizeof(JIF)));
|
|
|
|
acon.cpcur = cpcur;
|
|
acon.cicur = cicur;
|
|
|
|
acon.CURSORF_flags = CURSORF_ACON;
|
|
|
|
/*
|
|
* Store this information away so we can identify
|
|
* repeated calls to LoadCursor/Icon for the same
|
|
* resource type/id.
|
|
*/
|
|
acon.rt = PTR_TO_ID(rt);
|
|
acon.lpModName = szUSER32;
|
|
acon.lpName = (LPWSTR)pszName;
|
|
|
|
/*
|
|
* Make a private copy of the cursor pointers and the animation rate table.
|
|
*/
|
|
for (i = 0; i < cpcur; i++) {
|
|
ahcurT[i] = ahcur[i];
|
|
// ahcurT[i]->fPointer |= PTRI_ANIMATED; // if GDI needs it
|
|
|
|
}
|
|
|
|
for (i = 0; i < cicur; i++) {
|
|
|
|
/*
|
|
* If constant rate, initialize the rate table to a single value.
|
|
*/
|
|
if (ajifRate == NULL)
|
|
ajifRateT[i] = jifRate;
|
|
else
|
|
ajifRateT[i] = ajifRate[i];
|
|
|
|
/*
|
|
* If no sequence table then build a unity map to the cursor table.
|
|
*/
|
|
if (aicur == NULL)
|
|
aicurT[i] = i;
|
|
else
|
|
aicurT[i] = aicur[i];
|
|
}
|
|
|
|
/*
|
|
* Stuff acon data into the cursor
|
|
*/
|
|
if (!_SetCursorIconData(hacon, &acon)) {
|
|
NtUserDestroyCursor(hacon, CURSOR_ALWAYSDESTROY);
|
|
hacon = NULL;
|
|
}
|
|
UserLocalFree(ahcurT);
|
|
|
|
return hacon;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ReadIconFromFileMap
|
|
*
|
|
* LATER: Error handling.
|
|
*
|
|
* History:
|
|
* 12-21-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
HCURSOR ReadIconFromFileMap(
|
|
PFILEINFO pfi,
|
|
int cbSize, // used to seek past this chunk in case of error
|
|
DWORD cxDesired,
|
|
DWORD cyDesired,
|
|
DWORD LR_flags)
|
|
{
|
|
PCURSORRESOURCE pcres;
|
|
HCURSOR hcur = NULL;
|
|
LPNEWHEADER pnh;
|
|
int offResBase;
|
|
LPWSTR rt;
|
|
|
|
/*
|
|
* Get current position in file to be used as the base from which
|
|
* the icon data offsets are offset from.
|
|
*/
|
|
offResBase = (int)(pfi->pFilePtr - pfi->pFileMap);
|
|
|
|
/*
|
|
* Read the .ICO/.CUR data's header.
|
|
*/
|
|
ReadFilePtr(pfi, &pnh, sizeof(NEWHEADER));
|
|
|
|
pcres = ReadIconGuts(pfi,
|
|
pnh,
|
|
offResBase,
|
|
&rt,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags);
|
|
|
|
if (pcres != NULL) {
|
|
hcur = (HCURSOR)ConvertDIBIcon((LPBITMAPINFOHEADER)pcres,
|
|
NULL,
|
|
NULL,
|
|
(rt == RT_ICON),
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_ACONFRAME | LR_flags);
|
|
|
|
UserLocalFree(pcres);
|
|
}
|
|
|
|
/*
|
|
* Seek to the end of this chunk, regardless of our current position.
|
|
*/
|
|
pfi->pFilePtr = pfi->pFileMap + ((offResBase + cbSize + 1) & (~1));
|
|
|
|
return hcur;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* LoadAniIcon
|
|
*
|
|
* Loads an animatied cursor from a RIFF file. The RIFF file format for
|
|
* animated cursors looks like this:
|
|
*
|
|
* RIFF( 'ACON'
|
|
* LIST( 'INFO'
|
|
* INAM( <name> )
|
|
* IART( <artist> )
|
|
* )
|
|
* anih( <anihdr> )
|
|
* [rate( <rateinfo> ) ]
|
|
* ['seq '( <seq_info> )]
|
|
* LIST( 'fram' icon( <icon_file> ) ... )
|
|
* )
|
|
*
|
|
*
|
|
* History:
|
|
* 10-02-91 DarrinM Created.
|
|
* 03-17-93 JonPa Rewrote to use RIFF format instead of RAD
|
|
* 04-22-93 JonPa Finalized RIFF format (changed from ANI to ACON etc)
|
|
* 11/16/1995 SanfordS Added LR_flags support.
|
|
\***************************************************************************/
|
|
|
|
HICON LoadAniIcon(
|
|
IN PFILEINFO pfi,
|
|
IN LPWSTR rt,
|
|
IN DWORD cxDesired,
|
|
IN DWORD cyDesired,
|
|
IN DWORD LR_flags)
|
|
{
|
|
int cpcur, ipcur = 0, i, cicur;
|
|
ANIHEADER anih;
|
|
ANIHEADER *panih = NULL;
|
|
HICON hacon = NULL;
|
|
HCURSOR *phcur = NULL;
|
|
JIF jifRate, *pjifRate;
|
|
RTAG tag;
|
|
DWORD *picur;
|
|
|
|
/*
|
|
* Position to the beginning of the file.
|
|
*/
|
|
pfi->pFilePtr = pfi->pFileMap + sizeof(tag);
|
|
|
|
#if DBG
|
|
if ((ULONG_PTR)pfi->pFileEnd != ((ULONG_PTR)(pfi->pFileMap + sizeof (RTAG) + ((RTAG *)(pfi->pFileMap))->ckSize + 1) & ~1)) {
|
|
RIPMSG2(RIP_WARNING, "LoadAniIcon: First RIFF chunk has invalid ckSize. Actual:%#lx Expected:%#lx",
|
|
((RTAG *)(pfi->pFileMap))->ckSize, (pfi->pFileEnd - pfi->pFileMap - sizeof(RTAG)) & ~1);
|
|
}
|
|
#endif
|
|
|
|
/* read the chunk type */
|
|
if(!ReadFilePtrCopy(pfi,
|
|
&tag.ckID,
|
|
sizeof(tag.ckID))) {
|
|
goto laiFileErr;
|
|
}
|
|
|
|
if (tag.ckID != FOURCC_ACON)
|
|
goto laiFileErr;
|
|
|
|
/* look for 'anih', 'rate', 'seq ', and 'icon' chunks */
|
|
while( ReadTag(pfi, &tag)) {
|
|
|
|
switch( tag.ckID ) {
|
|
case FOURCC_anih:
|
|
if (!ReadChunk(pfi, &tag, &anih))
|
|
goto laiFileErr;
|
|
|
|
if (!(anih.fl & AF_ICON) || (anih.cFrames == 0))
|
|
goto laiFileErr;
|
|
|
|
/*
|
|
* Allocate space for the ANIHEADER, HCURSOR array and a
|
|
* rate table (in case we run into one later).
|
|
*/
|
|
cpcur = anih.cFrames;
|
|
cicur = anih.cSteps;
|
|
panih = (PANIHEADER)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(ANIHEADER));
|
|
|
|
if (panih == NULL)
|
|
goto laiFileErr;
|
|
|
|
phcur = UserLocalAlloc(HEAP_ZERO_MEMORY, cpcur * sizeof(HCURSOR) + cicur * sizeof(JIF) + cicur * sizeof(DWORD));
|
|
UserAssert(phcur == NATURAL_ALIGNED_PTR(HCURSOR, phcur));
|
|
if (phcur == NULL) {
|
|
goto laiFileErr;
|
|
}
|
|
|
|
pjifRate = NULL;
|
|
picur = NULL;
|
|
|
|
*panih = anih;
|
|
jifRate = panih->jifRate;
|
|
break;
|
|
|
|
|
|
case FOURCC_rate:
|
|
/*
|
|
* If we find a rate chunk, read it into its preallocated
|
|
* space.
|
|
*/
|
|
pjifRate = (PJIF)((PBYTE)phcur + cpcur * sizeof(HCURSOR));
|
|
UserAssert(pjifRate == NATURAL_ALIGNED_PTR(JIF, pjifRate));
|
|
if(!ReadChunk(pfi, &tag, (PBYTE)pjifRate))
|
|
goto laiFileErr;
|
|
break;
|
|
|
|
|
|
case FOURCC_seq:
|
|
/*
|
|
* If we find a seq chunk, read it into its preallocated
|
|
* space.
|
|
*/
|
|
picur = (DWORD *)((PBYTE)phcur + cpcur * sizeof(HCURSOR) +
|
|
cicur * sizeof(JIF));
|
|
UserAssert(picur == NATURAL_ALIGNED_PTR(DWORD, picur));
|
|
if(!ReadChunk(pfi, &tag, (PBYTE)picur))
|
|
goto laiFileErr;
|
|
break;
|
|
|
|
|
|
case FOURCC_LIST:
|
|
{
|
|
DWORD cbChunk = (tag.ckSize + 1) & ~1;
|
|
|
|
/*
|
|
* See if this list is the 'fram' list of icon chunks
|
|
*/
|
|
if(!ReadFilePtrCopy(pfi, &tag.ckID, sizeof(tag.ckID))) {
|
|
goto laiFileErr;
|
|
}
|
|
|
|
cbChunk -= sizeof(tag.ckID);
|
|
|
|
if (tag.ckID != FOURCC_fram) {
|
|
/*
|
|
* Not the fram list (probably the INFO list). Skip
|
|
* the rest of this chunk. (Don't forget that we have
|
|
* already skipped one dword!)
|
|
*/
|
|
tag.ckSize = cbChunk;
|
|
SkipChunk(pfi, &tag);
|
|
break;
|
|
}
|
|
|
|
while(cbChunk >= sizeof(tag)) {
|
|
if (!ReadTag(pfi, &tag))
|
|
goto laiFileErr;
|
|
|
|
cbChunk -= sizeof(tag);
|
|
|
|
if(tag.ckID == FOURCC_icon) {
|
|
|
|
/*
|
|
* Ok, load the icon/cursor bits, create a cursor from
|
|
* them, and save a pointer to it away in the ACON
|
|
* cursor pointer array.
|
|
*/
|
|
phcur[ipcur] = ReadIconFromFileMap(pfi,
|
|
tag.ckSize,
|
|
cxDesired,
|
|
cyDesired,
|
|
LR_flags);
|
|
|
|
if (phcur[ipcur] == NULL) {
|
|
for (i = 0; i < ipcur; i++)
|
|
NtUserDestroyCursor(phcur[i], 0);
|
|
goto laiFileErr;
|
|
}
|
|
|
|
ipcur++;
|
|
} else {
|
|
/*
|
|
* Unknown chunk in fram list, just ignore it
|
|
*/
|
|
SkipChunk(pfi, &tag);
|
|
}
|
|
|
|
cbChunk -= (tag.ckSize + 1) & ~1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* We're not interested in this chunk, skip it.
|
|
*/
|
|
if(!SkipChunk(pfi, &tag))
|
|
goto laiFileErr;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Sanity check the count of frames so we won't fault trying
|
|
* to select a nonexistant cursor
|
|
*/
|
|
if (cpcur != ipcur) {
|
|
RIPMSG2(RIP_WARNING, "LoadAniIcon: Invalid number of frames; Actual:%#lx Expected:%#lx",
|
|
ipcur, cpcur);
|
|
for (i = 0; i < ipcur; i++)
|
|
NtUserDestroyCursor(phcur[i], CURSOR_ALWAYSDESTROY);
|
|
goto laiFileErr;
|
|
}
|
|
|
|
|
|
|
|
if (cpcur != 0)
|
|
hacon = CreateAniIcon(pfi->pszName,
|
|
rt,
|
|
cicur,
|
|
picur,
|
|
cpcur,
|
|
phcur,
|
|
jifRate,
|
|
pjifRate,
|
|
LR_flags & LR_GLOBAL);
|
|
|
|
laiFileErr:
|
|
|
|
#if DBG
|
|
if (hacon == NULL) {
|
|
RIPMSG0(RIP_WARNING, "LoadAniIcon: Invalid icon data format");
|
|
}
|
|
#endif
|
|
|
|
if (panih != NULL)
|
|
UserLocalFree(panih);
|
|
|
|
if (phcur != NULL) {
|
|
UserLocalFree(phcur);
|
|
}
|
|
|
|
return hacon;
|
|
}
|