/****************************************************************************/
/* */
/* 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;
}