/****************************************************************************/ /* */ /* 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 #include // For NT fstat(). #include // For fstat() types. #include // 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; }