RWICOCUR.C

/****************************************************************************/ 
/* */
/* Copyright (C) 1987-1996 Microsoft Corp. */
/* All Rights Reserved */
/* */
/****************************************************************************/
/****************************** Module Header *******************************
* Module Name: rwicocur.c
*
* Routines to read and write icon and cursor files.
*
* History:
*
****************************************************************************/

#include "imagedit.h"

#include <io.h>
#include <fcntl.h> // For NT fstat().
#include <sys\types.h> // For fstat() types.
#include <sys\stat.h> // For fstat() function.



/************************************************************************
* LoadIconCursorFile
*
* Loads the specified icon or cursor file. It reads the images into
* a list, then prompts for which one to open initially.
*
* Arguments:
*
* History:
*
************************************************************************/

BOOL LoadIconCursorFile(
PSTR pszFullFileName,
BOOL fIcon)
{
HFILE hf;
INT i;
PIMAGEINFO pImage;
LPBITMAPINFO lpBitmapInfo;
HANDLE hDIB; // Handle to DIB bits.
OFSTRUCT OfStruct;
struct stat FileStatus;
ICOCURSORHDR hdr; // Header structure of icon/cursor file.
INT nImages;
PICOCURSORDESC aIcoCurDesc; // Array of ico/cur descriptors.
DWORD dwFilePos;
DWORD dwFileSize;
INT iType;

if ((hf = (HFILE)OpenFile(pszFullFileName, (LPOFSTRUCT)&OfStruct, OF_READ))
== (HFILE)-1) {
Message(MSG_CANTOPEN, pszFullFileName);
return FALSE;
}

fstat((INT)_open_osfhandle((long)(hf), (int)(O_RDONLY)), &FileStatus);
dwFileSize = (DWORD)FileStatus.st_size;

ImageLinkFreeList();

if (fIcon)
iType = FT_ICON;
else
iType = FT_CURSOR;

/*
* Read the Icon/Cursor File header.
*/
if (!MyFileRead(hf, (LPSTR)&hdr, sizeof(ICOCURSORHDR),
pszFullFileName, iType))
goto Error1;

if (hdr.iReserved != 0) {
Message(MSG_BADICOCURFILE, pszFullFileName);
goto Error1;
}

/*
* Get number of images in the file.
*/
nImages = hdr.iResourceCount;

if (!nImages || nImages > MAXIMAGES) {
Message(MSG_BADICOCURFILE, pszFullFileName);
goto Error1;
}

if (hdr.iResourceType != 1 && hdr.iResourceType != 2) {
Message(MSG_BADICOCURFILE, pszFullFileName);
goto Error1;
}

/*
* Allocate room for the descriptor records.
*/
if (!(aIcoCurDesc = (PICOCURSORDESC)MyAlloc(
sizeof(ICOCURSORDESC) * nImages)))
goto Error1;

/*
* Read in the descriptor records.
*/
if (!MyFileRead(hf, (LPSTR)aIcoCurDesc, sizeof(ICOCURSORDESC) * nImages,
pszFullFileName, iType))
goto Error2;

/*
* Get the current file position (after the descriptors). This
* should be the start of the DIB's.
*/
dwFilePos = (DWORD)SetFilePointer((HANDLE)hf, 0, NULL, (DWORD)1);

/*
* Validate the descriptor records.
*/
for (i = 0; i < nImages; i++) {
/*
* Make sure the DIB's are sequential (not overlapping)
* and they all fit within the file.
*/
if (aIcoCurDesc[i].DIBOffset != dwFilePos ||
dwFilePos + aIcoCurDesc[i].DIBSize > dwFileSize) {
Message(MSG_BADICOCURFILE, pszFullFileName);
goto Error2;
}

/*
* Jump to the next DIB.
*/
dwFilePos += aIcoCurDesc[i].DIBSize;
}

for (i = 0; i < nImages; i++) {
pImage = ImageLinkAlloc(NULL, 0, 0,
aIcoCurDesc[i].iHotspotX, aIcoCurDesc[i].iHotspotY,
(aIcoCurDesc[i].iColorCount == (BYTE)8) ?
aIcoCurDesc[i].iColorCount : 0);

if (!pImage)
goto Error3;

/*
* Allocate space for the DIB for this image.
*/
if (!(hDIB = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
(DWORD)aIcoCurDesc[i].DIBSize))) {
Message(MSG_OUTOFMEMORY);
goto Error3;
}

pImage->DIBSize = aIcoCurDesc[i].DIBSize;
pImage->DIBhandle = hDIB;
pImage->DIBPtr = (LPSTR)GlobalLock(hDIB);
}

for (pImage = gpImageHead; pImage != NULL; pImage = pImage->pImageNext) {
if (!MyFileRead(hf, pImage->DIBPtr, (DWORD)pImage->DIBSize,
pszFullFileName, iType))
goto Error3;

lpBitmapInfo = (LPBITMAPINFO)pImage->DIBPtr;

if (!IsValidDIB(lpBitmapInfo, pImage->DIBSize, TRUE)) {
Message(MSG_BADICOCURFILE, pszFullFileName);
goto Error3;
}

/*
* Fill the x and y size fields in image node from
* information in the DIB header.
*/
pImage->cx = (INT)lpBitmapInfo->bmiHeader.biWidth;
pImage->cy = (INT)lpBitmapInfo->bmiHeader.biHeight / 2;
if (pImage->nColors == 0)
pImage->nColors = (1 << lpBitmapInfo->bmiHeader.biBitCount);

pImage->pDevice = DeviceLinkFind(
fIcon ? gpIconDeviceHead : gpCursorDeviceHead,
pImage->nColors, pImage->cx, pImage->cy);
}

_lclose((HFILE)hf);

fFileDirty = FALSE;
SetFileName(pszFullFileName);
giType = iType;

gnImages = nImages;

/*
* Update the PropBar and the Toolbox so that they show
* information about the opened file. We do this now just
* in case the user cancels out of the Image Select Dialog.
*/
PropBarUpdate();
ToolboxUpdate();

/*
* Open up an image. If there are multiple images in the file,
* show the Image Select dialog. We also show the Image Select
* dialog if the file only has one image but it is not for a known
* device.
*/
if (gnImages > 1 || !gpImageHead->pDevice)
ImageSelectDialog();
else
ImageOpen2(gpImageHead);

return TRUE;

Error3:
ImageLinkFreeList();

Error2:
MyFree(aIcoCurDesc);

Error1:
_lclose((HFILE)hf);

return FALSE;
}



/************************************************************************
* IsValidDIB
*
* This function determines if the given DIB is valid or not. It does
* this without touching memory outside the bounds of the cbDIBSize
* passed in or the size of a BITMAPINFOHEADER, whichever is smaller.
* Note that even if the DIB is valid, however, the current image
* editor might not be able to edit it (the size might be too big, for
* instance).
*
* Arguments:
* LPBITMAPINFO pDIB - Points to the DIB.
* DWORD cbDIBSize - The size of the DIB.
* BOOL fIcoCur - TRUE if this is an icon or cursor. This effects
* whether an AND mask is expected to be in the DIB.
*
* History:
*
************************************************************************/

BOOL IsValidDIB(
LPBITMAPINFO pDIB,
DWORD cbDIBSize,
BOOL fIcoCur)
{
DWORD cbANDMask;
DWORD cbXORMask;
DWORD cbColorTable;
DWORD nHeight;

if (cbDIBSize < sizeof(BITMAPINFOHEADER))
return FALSE;

if (pDIB->bmiHeader.biSize != sizeof(BITMAPINFOHEADER))
return FALSE;

if (pDIB->bmiHeader.biPlanes != 1)
return FALSE;

if (pDIB->bmiHeader.biBitCount != 1 &&
pDIB->bmiHeader.biBitCount != 4 &&
pDIB->bmiHeader.biBitCount != 8 &&
pDIB->bmiHeader.biBitCount != 24)
return FALSE;

if (fIcoCur) {
nHeight = pDIB->bmiHeader.biHeight / 2;
cbANDMask = (((pDIB->bmiHeader.biWidth + 31) & 0xffffffe0) >> 3) *
nHeight;
}
else {
nHeight = pDIB->bmiHeader.biHeight;
cbANDMask = 0;
}

cbColorTable = (1 << pDIB->bmiHeader.biBitCount) * sizeof(RGBQUAD);
cbXORMask = ((((pDIB->bmiHeader.biWidth * pDIB->bmiHeader.biBitCount) +
31) & 0xffffffe0) >> 3) * nHeight;

/*
* Check the size field in the header. This must be either zero
* or a valid size.
*/
if (pDIB->bmiHeader.biSizeImage &&
pDIB->bmiHeader.biSizeImage != cbXORMask + cbANDMask)
return FALSE;

if (cbDIBSize != sizeof(BITMAPINFOHEADER) + cbColorTable +
cbXORMask + cbANDMask)
return FALSE;

return TRUE;
}



/************************************************************************
* SaveIconCursorFile
*
*
*
* Arguments:
*
* Returns:
* TRUE if successful, FALSE otherwise.
*
* History:
*
************************************************************************/

BOOL SaveIconCursorFile(
PSTR pszFullFileName,
INT iType)
{
ICOCURSORHDR IcoCurHdr; // Header structure of icon/cursor file.
ICOCURSORDESC IcoCurDesc; // Icon/cursor descriptor struct.
HCURSOR hcurOld; // Handle to old cursor.
PIMAGEINFO pImage; // Pointer to node in image list.
DWORD iBitsOffset; // Offset of the actual DIB bits for image.
HFILE hf;
OFSTRUCT OfStruct;

hcurOld = SetCursor(hcurWait);

/*
* Save the bits of the current image.
*/
ImageSave();

/*
* Open the file for writing.
*/
if ((hf = (HFILE)OpenFile(pszFullFileName, &OfStruct, OF_CREATE | OF_READWRITE))
== (HFILE)-1) {
Message(MSG_CANTCREATE, pszFullFileName);
goto Error1;
}

/*
* This is crucial since this helps distinguish a 3.0 icon/cursor
* from an old, old (2.1 format) icon/cursor, which has meaningful
* information in this WORD.
*/
IcoCurHdr.iReserved = (WORD)0;

if (iType == FT_ICON)
IcoCurHdr.iResourceType = 1; // Icon type.
else
IcoCurHdr.iResourceType = 2; // Cursor type.

IcoCurHdr.iResourceCount = (WORD)gnImages;

/*
* Write the header to disk.
*/
if (!MyFileWrite(hf, (LPSTR)&IcoCurHdr, sizeof(ICOCURSORHDR),
pszFullFileName))
goto Error2;

/*
* Write all the descriptors.
*/
iBitsOffset = sizeof(ICOCURSORHDR) + (gnImages * sizeof(ICOCURSORDESC));
for (pImage = gpImageHead; pImage; pImage = pImage->pImageNext) {
IcoCurDesc.iWidth = (BYTE)pImage->cx;
IcoCurDesc.iHeight = (BYTE)pImage->cy;
IcoCurDesc.iColorCount = (giType == FT_ICON) ?
(BYTE)pImage->nColors : (BYTE)0;
IcoCurDesc.iUnused = 0;
IcoCurDesc.iHotspotX = (WORD)pImage->iHotspotX;
IcoCurDesc.iHotspotY = (WORD)pImage->iHotspotY;
IcoCurDesc.DIBSize = pImage->DIBSize;
IcoCurDesc.DIBOffset = iBitsOffset;

if (!MyFileWrite(hf, (LPSTR)&IcoCurDesc, sizeof(ICOCURSORDESC),
pszFullFileName))
goto Error2;

iBitsOffset += IcoCurDesc.DIBSize;
}

/*
* Now write the DIB's.
*/
for (pImage = gpImageHead; pImage; pImage = pImage->pImageNext) {
if (!MyFileWrite(hf, (LPSTR)pImage->DIBPtr,
(DWORD)pImage->DIBSize, pszFullFileName))
goto Error2;
}

_lclose((HFILE)hf);

fFileDirty = FALSE;
SetFileName(pszFullFileName);

SetCursor(hcurOld);

return TRUE;

Error2:
_lclose((HFILE)hf);

Error1:
SetCursor(hcurOld);

return FALSE;
}