422 lines
12 KiB
C
422 lines
12 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* Microsoft Confidential */
|
|
/* */
|
|
/* Copyright (c) Microsoft Corp. 1987, 1991 */
|
|
/* 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 = MOpenFile(pszFullFileName, (LPOFSTRUCT)&OfStruct, OF_READ))
|
|
== (HFILE)-1) {
|
|
Message(MSG_CANTOPEN, pszFullFileName);
|
|
return FALSE;
|
|
}
|
|
|
|
_fstat(HFILE2INT(hf, 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)M_llseek(hf, 0, 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, (WORD2DWORD)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);
|
|
}
|
|
|
|
M_lclose(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:
|
|
M_lclose(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.
|
|
*/
|
|
// Disabled because Win95 icons seem to have the wrong value here and
|
|
// nobody noticed (oops!)
|
|
//
|
|
// 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 = MOpenFile(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,
|
|
(WORD2DWORD)pImage->DIBSize, pszFullFileName))
|
|
goto Error2;
|
|
}
|
|
|
|
M_lclose(hf);
|
|
|
|
fFileDirty = FALSE;
|
|
SetFileName(pszFullFileName);
|
|
|
|
SetCursor(hcurOld);
|
|
|
|
return TRUE;
|
|
|
|
Error2:
|
|
M_lclose(hf);
|
|
|
|
Error1:
|
|
SetCursor(hcurOld);
|
|
|
|
return FALSE;
|
|
}
|