// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection //******************************************************************* // // file.c // // Source file for Device-Independent Bitmap (DIB) API. Provides // the following functions: // // SaveDIB() - Saves the specified dib in a file // LoadDIB() - Loads a DIB from a file // DestroyDIB() - Deletes DIB when finished using it // // Development Team: Mark Bader // Patrick Schreiber // Garrett McAuliffe // Eric Flo // Tony Claflin // // Written by Microsoft Product Support Services, Developer Support. // COPYRIGHT: // // (C) Copyright Microsoft Corp. 1993. All rights reserved. // // You have a royalty-free right to use, modify, reproduce and // distribute the Sample Files (and/or any modified version) in // any way you find useful, provided that you agree that // Microsoft has no warranty obligations or liability for any // Sample Application Files which are modified. // //******************************************************************* #if (0) // ST - 5/8/2019 #include #include #include #include #include #include #include #include "dibutil.h" #include "dibapi.h" //#include "WolDebug.h" /* * Dib Header Marker - used in writing DIBs to files */ #define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') /********************************************************************* * * Local Function Prototypes * *********************************************************************/ HANDLE ReadDIBFile(int); BOOL MyRead(int, LPSTR, DWORD); BOOL SaveDIBFile(void); BOOL WriteDIB(LPSTR, HANDLE); DWORD PASCAL MyWrite(int, VOID FAR *, DWORD); /************************************************************************* * * LoadDIB() * * Loads the specified DIB from a file, allocates memory for it, * and reads the disk file into the memory. * * * Parameters: * * LPSTR lpFileName - specifies the file to load a DIB from * * Returns: A handle to a DIB, or NULL if unsuccessful. * * NOTE: The DIB API were not written to handle OS/2 DIBs; This * function will reject any file that is not a Windows DIB. * * History: Date Author Reason * 9/15/91 Mark Bader Based on DIBVIEW * *************************************************************************/ HDIB FAR LoadDIB(LPSTR lpFileName) { HDIB hDIB; int hFile; OFSTRUCT ofs; /* * Set the cursor to a hourglass, in case the loading operation * takes more than a sec, the user will know what's going on. */ SetCursor(LoadCursor(NULL, IDC_WAIT)); if ((hFile = OpenFile(lpFileName, &ofs, OF_READ)) != -1) { hDIB = ReadDIBFile(hFile); _lclose(hFile); SetCursor(LoadCursor(NULL, IDC_ARROW)); return hDIB; } else { // DIBError(ERR_FILENOTFOUND); SetCursor(LoadCursor(NULL, IDC_ARROW)); return NULL; } } /************************************************************************* * * SaveDIB() * * Saves the specified DIB into the specified file name on disk. No * error checking is done, so if the file already exists, it will be * written over. * * Parameters: * * HDIB hDib - Handle to the dib to save * * LPSTR lpFileName - pointer to full pathname to save DIB under * * Return value: 0 if successful, or one of: * ERR_INVALIDHANDLE * ERR_OPEN * ERR_LOCK * * History: * * NOTE: The DIB API were not written to handle OS/2 DIBs, so this * function will not save a file if it is not a Windows DIB. * * History: Date Author Reason * 9/15/91 Mark Bader Taken from DIBVIEW (which was taken * from SHOWDIB) * 1/30/92 Mark Bader Fixed problem of writing too many * bytes to the file * 6/24/92 Mark Bader Added check for OS/2 DIB * *************************************************************************/ WORD FAR SaveDIB(HDIB hDib, LPSTR lpFileName) { BITMAPFILEHEADER bmfHdr; // Header for Bitmap file LPBITMAPINFOHEADER lpBI; // Pointer to DIB info structure int fh; // file handle for opened file OFSTRUCT of; // OpenFile structure DWORD dwDIBSize; DWORD dwError; // Error return from MyWrite if (!hDib) return ERR_INVALIDHANDLE; fh = OpenFile(lpFileName, &of, OF_CREATE | OF_READWRITE); if (fh == -1) return ERR_OPEN; /* * Get a pointer to the DIB memory, the first of which contains * a BITMAPINFO structure */ lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib); if (!lpBI) return ERR_LOCK; // Check to see if we're dealing with an OS/2 DIB. If so, don't // save it because our functions aren't written to deal with these // DIBs. if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) { GlobalUnlock(hDib); return ERR_NOT_DIB; } /* * Fill in the fields of the file header */ /* Fill in file type (first 2 bytes must be "BM" for a bitmap) */ bmfHdr.bfType = DIB_HEADER_MARKER; // "BM" // Calculating the size of the DIB is a bit tricky (if we want to // do it right). The easiest way to do this is to call GlobalSize() // on our global handle, but since the size of our global memory may have // been padded a few bytes, we may end up writing out a few too // many bytes to the file (which may cause problems with some apps, // like HC 3.0). // // So, instead let's calculate the size manually. // // To do this, find size of header plus size of color table. Since the // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains // the size of the structure, let's use this. dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPSTR)lpBI); // Partial Calculation // Now calculate the size of the image if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) { // It's an RLE bitmap, we can't calculate size, so trust the // biSizeImage field dwDIBSize += lpBI->biSizeImage; } else { DWORD dwBmBitsSize; // Size of Bitmap Bits only // It's not RLE, so size is Width (DWORD aligned) * Height dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * lpBI->biHeight; dwDIBSize += dwBmBitsSize; // Now, since we have calculated the correct size, why don't we // fill in the biSizeImage field (this will fix any .BMP files which // have this field incorrect). lpBI->biSizeImage = dwBmBitsSize; } // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER) bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; /* * Now, calculate the offset the actual bitmap bits will be in * the file -- It's the Bitmap file header plus the DIB header, * plus the size of the color table. */ bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + PaletteSize((LPSTR)lpBI); /* Write the file header */ _lwrite(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER)); /* * Write the DIB header and the bits -- use local version of * MyWrite, so we can write more than 32767 bytes of data */ dwError = MyWrite(fh, (LPSTR)lpBI, dwDIBSize); GlobalUnlock(hDib); _lclose(fh); if (dwError == 0) return ERR_OPEN; // oops, something happened in the write else return 0; // Success code } /************************************************************************* * * DestroyDIB () * * Purpose: Frees memory associated with a DIB * * Returns: Nothing * * History: Date Author Reason * 9/15/91 Mark Bader Created * *************************************************************************/ WORD FAR DestroyDIB(HDIB hDib) { GlobalFree(hDib); return 0; } //************************************************************************ // // Auxiliary Functions which the above procedures use // //************************************************************************ /************************************************************************* * * Function: ReadDIBFile (int) * * Purpose: Reads in the specified DIB file into a global chunk of * memory. * * Returns: A handle to a dib (hDIB) if successful. * NULL if an error occurs. * * Comments: BITMAPFILEHEADER is stripped off of the DIB. Everything * from the end of the BITMAPFILEHEADER structure on is * returned in the global memory handle. * * * NOTE: The DIB API were not written to handle OS/2 DIBs, so this * function will reject any file that is not a Windows DIB. * * History: Date Author Reason * 9/15/91 Mark Bader Based on DIBVIEW * 6/25/92 Mark Bader Added check for OS/2 DIB * 7/21/92 Mark Bader Added code to deal with bfOffBits * field in BITMAPFILEHEADER * 9/11/92 Mark Bader Fixed Realloc Code to free original mem * *************************************************************************/ HANDLE ReadDIBFile(int hFile) { BITMAPFILEHEADER bmfHeader; DWORD dwBitsSize; UINT nNumColors; // Number of colors in table HANDLE hDIB; HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB LPBITMAPINFOHEADER lpbi; DWORD offBits; /* * get length of DIB in bytes for use when reading */ dwBitsSize = filelength(hFile); // Allocate memory for header & color table. We'll enlarge this // memory as needed. hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); if (!hDIB) return NULL; lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); if (!lpbi) { GlobalFree(hDIB); return NULL; } // read the BITMAPFILEHEADER from our file if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) goto ErrExit; if (bmfHeader.bfType != 0x4d42) /* 'BM' */ goto ErrExit; // read the BITMAPINFOHEADER if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) goto ErrExit; // Check to see that it's a Windows DIB -- an OS/2 DIB would cause // strange problems with the rest of the DIB API since the fields // in the header are different and the color table entries are // smaller. // // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) goto ErrExit; // Now determine the size of the color table and read it. Since the // bitmap bits are offset in the file by bfOffBits, we need to do some // special processing here to make sure the bits directly follow // the color table (because that's the format we are susposed to pass // back) nNumColors = (UINT)lpbi->biClrUsed; if (!nNumColors) { // no color table for 24-bit, default size otherwise if (lpbi->biBitCount != 24) nNumColors = 1 << lpbi->biBitCount; /* standard size table */ } // fill in some default values if they are zero if (lpbi->biClrUsed == 0) lpbi->biClrUsed = nNumColors; if (lpbi->biSizeImage == 0) { lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; } // get a proper-sized buffer for header, color table and bits GlobalUnlock(hDIB); hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); if (!hDIBtmp) // can't resize buffer for loading goto ErrExitNoUnlock; //MPB else hDIB = hDIBtmp; lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); // read the color table _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); // offset to the bits from start of DIB header offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); // If the bfOffBits field is non-zero, then the bits might *not* be // directly following the color table in the file. Use the value in // bfOffBits to seek the bits. if (bmfHeader.bfOffBits != 0L) _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) goto OKExit; ErrExit: GlobalUnlock(hDIB); ErrExitNoUnlock: GlobalFree(hDIB); return NULL; OKExit: GlobalUnlock(hDIB); return hDIB; } /************************************************************************* Function: MyRead (int, LPSTR, DWORD) Purpose: Routine to read files greater than 64K in size. Returns: TRUE if successful. FALSE if an error occurs. History: Date Author Reason 9/15/91 Mark Bader Based on DIBVIEW *************************************************************************/ BOOL MyRead(int hFile, LPSTR lpBuffer, DWORD dwSize) { char huge *lpInBuf = (char huge *)lpBuffer; int nBytes; /* * Read in the data in 32767 byte chunks (or a smaller amount if it's * the last chunk of data read) */ while (dwSize) { nBytes = (int)(dwSize > (DWORD)32767 ? 32767 : LOWORD (dwSize)); if (_lread(hFile, (LPSTR)lpInBuf, nBytes) != (WORD)nBytes) return FALSE; dwSize -= nBytes; lpInBuf += nBytes; } return TRUE; } /**************************************************************************** FUNCTION : MyWrite(int fh, VOID FAR *pv, DWORD ul) PURPOSE : Writes data in steps of 32k till all the data is written. Normal _lwrite uses a WORD as 3rd parameter, so it is limited to 32767 bytes, but this procedure is not. RETURNS : 0 - If write did not proceed correctly. number of bytes written otherwise. History: Date Author Reason 9/15/91 Mark Bader Based on DIBVIEW ****************************************************************************/ DWORD PASCAL MyWrite(int iFileHandle, VOID FAR *lpBuffer, DWORD dwBytes) { DWORD dwBytesTmp = dwBytes; // Save # of bytes for return value BYTE huge *hpBuffer = (BYTE huge *)lpBuffer; // make a huge pointer to the data /* * Write out the data in 32767 byte chunks. */ while (dwBytes > 32767) { if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)32767) != 32767) return 0; dwBytes -= 32767; hpBuffer += 32767; } /* Write out the last chunk (which is < 32767 bytes) */ if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)dwBytes) != (WORD)dwBytes) return 0; return dwBytesTmp; } // ajw added // Added to allow "loading" from a location in memory. // A modification of ReadDIBFile(), above. //*********************************************************************************************** HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ) { BITMAPFILEHEADER bmfHeader; UINT nNumColors; // Number of colors in table HANDLE hDIB; HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB LPBITMAPINFOHEADER lpbi; DWORD offBits; const unsigned char* const pDataStart = pData; const unsigned char* pDataEnd = pData + dwBitsSize; // One char past end of "file". // Allocate memory for header & color table. We'll enlarge this // memory as needed. // debugprint( "LoadDIB_FromMemory, GlobalAlloc\n" ); hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); // debugprint( "hDIB from GlobalALloc is %i\n", hDIB ); if (!hDIB) { // debugprint( "LoadDIB_FromMemory error: failed alloc\n" ); return NULL; } // debugprint( "LoadDIB_FromMemory, lpbi Lock\n" ); lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); // debugprint( "lpbi is %i\n", lpbi ); if (!lpbi) { // debugprint( "LoadDIB_FromMemory error: failed lock\n" ); GlobalFree(hDIB); return NULL; } // read the BITMAPFILEHEADER from our file // if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) // goto ErrExit; if( pData + sizeof( BITMAPFILEHEADER ) >= pDataEnd ) { // debugprint( "LoadDIB_FromMemory error: bad size\n" ); goto ErrExit; } // debugprint( "LoadDIB_FromMemory, memcpy BITMAPFILEHEADER %i bytes\n", sizeof( BITMAPFILEHEADER ) ); memcpy( &bmfHeader, pData, sizeof( BITMAPFILEHEADER ) ); pData += sizeof( BITMAPFILEHEADER ); if (bmfHeader.bfType != 0x4d42) /* 'BM' */ { // debugprint( "LoadDIB_FromMemory error: no BM\n" ); goto ErrExit; } // read the BITMAPINFOHEADER // if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) // goto ErrExit; if( pData + sizeof( BITMAPINFOHEADER ) >= pDataEnd ) { // debugprint( "LoadDIB_FromMemory error: bad size 2\n" ); goto ErrExit; } // debugprint( "LoadDIB_FromMemory, memcpy BITMAPINFOHEADER %i bytes\n", sizeof( BITMAPINFOHEADER ) ); memcpy( lpbi, pData, sizeof( BITMAPINFOHEADER ) ); pData += sizeof( BITMAPINFOHEADER ); // Check to see that it's a Windows DIB -- an OS/2 DIB would cause // strange problems with the rest of the DIB API since the fields // in the header are different and the color table entries are // smaller. // // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) { // debugprint( "LoadDIB_FromMemory error: lpbi->biSize bad\n" ); goto ErrExit; } if( lpbi->biCompression != BI_RGB ) { // debugprint( "LoadDIB_FromMemory error: Image is compressed\n" ); goto ErrExit; } // Now determine the size of the color table and read it. Since the // bitmap bits are offset in the file by bfOffBits, we need to do some // special processing here to make sure the bits directly follow // the color table (because that's the format we are susposed to pass // back) nNumColors = (UINT)lpbi->biClrUsed; if (!nNumColors) { // no color table for 24-bit, default size otherwise if (lpbi->biBitCount != 24) nNumColors = 1 << lpbi->biBitCount; /* standard size table */ } // fill in some default values if they are zero if (lpbi->biClrUsed == 0) lpbi->biClrUsed = nNumColors; // debugprint( "biSizeImage is %i. I would say it was %i, because the bpp is %i.\n", lpbi->biSizeImage, ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight, lpbi->biBitCount ); if (lpbi->biSizeImage == 0) { lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; } // get a proper-sized buffer for header, color table and bits GlobalUnlock(hDIB); // debugprint( "LoadDIB_FromMemory, GlobalReAlloc: lpbi->biSize=%i, nNumColors=%i, lpbi->biSizeImage=%i\n", lpbi->biSize, nNumColors,lpbi->biSizeImage ); hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); if (!hDIBtmp) // can't resize buffer for loading { // debugprint( "LoadDIB_FromMemory error: realloc failed\n" ); goto ErrExitNoUnlock; //MPB } else hDIB = hDIBtmp; lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); // read the color table // _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); // debugprint( "LoadDIB_FromMemory, memcpy color table %i colors, so %i bytes\n", nNumColors, nNumColors * sizeof(RGBQUAD) ); memcpy( (LPSTR)(lpbi) + lpbi->biSize, pData, nNumColors * sizeof(RGBQUAD) ); pData += nNumColors * sizeof(RGBQUAD); // offset to the bits from start of DIB header offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); // If the bfOffBits field is non-zero, then the bits might *not* be // directly following the color table in the file. Use the value in // bfOffBits to seek the bits. if (bmfHeader.bfOffBits != 0L) // _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); pData = pDataStart + bmfHeader.bfOffBits; // debugprint( "bmfHeader.bfOffBits is %i\n", bmfHeader.bfOffBits ); // if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) // goto OKExit; // debugprint( "Checking that pData(%i) + biSizeImage(%i), which is %i, is equal to pDataEnd(%i)\n", // pData, lpbi->biSizeImage, pData + lpbi->biSizeImage, pDataEnd ); // if( pData + lpbi->biSizeImage != pDataEnd ) condition relaxed // { // debugprint( "LoadDIB_FromMemory error: bad size 3\n" ); // goto ErrExit; // } // debugprint( "LoadDIB_FromMemory, memcpy the bits, %i bytes. Image is w %i, h.%i\n", // lpbi->biSizeImage, lpbi->biWidth, lpbi->biHeight ); // debugprint( "Writing to lpbi (%i) + offBits (%i)\n", lpbi, offBits ); memcpy( (LPSTR)lpbi + offBits, pData, lpbi->biSizeImage ); // pData += lpbi->biSizeImage; // if( pData != pDataEnd ) // Should end up one byte past end of data. - condition relaxed // debugprint( "LoadDIB_FromMemory: ERROR! Ended up at %i instead of %i\n", pData, pDataEnd ); goto OKExit; ErrExit: GlobalUnlock(hDIB); ErrExitNoUnlock: GlobalFree(hDIB); // debugprint( "LoadDIB_FromMemory Error!\n" ); return NULL; OKExit: GlobalUnlock(hDIB); return hDIB; } #endif