//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Implementation of the IEditorTexture interface for WAD textures. // //=============================================================================// #include "stdafx.h" #include #include #include #include #include #include "hammer.h" #include "MapDoc.h" #include "Options.h" #include "MainFrm.h" #include "GlobalFunctions.h" #include "WADTypes.h" #include "BSPFile.h" #include "bitmap/imageformat.h" // hack : don't want to include this just for ImageFormat #include "TextureSystem.h" #include "WADTexture.h" // memdbgon must be the last include file in a .cpp file!!! #include #pragma warning(disable:4244) #define _GraphicCacheAllocate(n) malloc(n) //----------------------------------------------------------------------------- // Stuff for loading WAD3 files. //----------------------------------------------------------------------------- typedef struct WAD3miptex_s { char name[16]; unsigned width, height; unsigned offsets[4]; // four mip maps stored } WAD3miptex_t; //----------------------------------------------------------------------------- // Stuff for loading WAL files. //----------------------------------------------------------------------------- typedef struct // Mip Graphic { char name[32]; // Name of the Graphic. DWORD width; // width of picture, must be a multiple of 8 DWORD height; // height of picture, must be a multiple of 8 DWORD offset1; // offset to u_char Pix[width * height] DWORD offset2; // offset to u_char Pix[width/2 * height/2] DWORD offset4; // offset to u_char Pix[width/4 * height/4] DWORD offset8; // offset to u_char Pix[width/8 * height/8] char animname[32]; DWORD surface; DWORD contents; DWORD value; } walhdr_t; static char *g_pLoadBuf = NULL; static int g_nLoadSize = 128 * 1024; extern void ScaleBitmap(CSize sizeSrc, CSize sizeDest, char *src, char *dest); //----------------------------------------------------------------------------- // Purpose: // Input : nSize - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- static bool AllocateLoadBuffer(int nSize) { if (nSize > g_nLoadSize) { g_nLoadSize = nSize; if (g_pLoadBuf != NULL) { delete[] g_pLoadBuf; g_pLoadBuf = NULL; } } if (g_pLoadBuf == NULL) { g_pLoadBuf = new char[g_nLoadSize]; } if (g_pLoadBuf == NULL) { return(false); } return(true); } //----------------------------------------------------------------------------- // Purpose: Constructor. Initializes data members. //----------------------------------------------------------------------------- CWADTexture::CWADTexture(void) { memset(m_szName, 0, sizeof(m_szName)); memset(m_szFileName, 0, sizeof(m_szFileName)); m_datawidth = 0; m_dataheight = 0; m_WALsurface = 0; m_WALvalue = 0; m_WALcontents = 0; m_ulFileOffset = 0; m_ulFileID = 0; memset(&format, 0, sizeof(format)); m_pPalette = NULL; m_bLocalPalette = false; m_nWidth = 0; m_nHeight = 0; m_pData = NULL; } //----------------------------------------------------------------------------- // Purpose: Destructor. Frees texture image data and palette. //----------------------------------------------------------------------------- CWADTexture::~CWADTexture(void) { // // Free image data. // if (m_pData != NULL) { free(m_pData); m_pData = NULL; } // // Free palette. // if (m_pPalette != NULL) { free(m_pPalette); m_pPalette = NULL; } } //----------------------------------------------------------------------------- // Purpose: Returns the full path of the file (WAD, PAK, or WAL) from which this // texture was loaded. //----------------------------------------------------------------------------- const char *CWADTexture::GetFileName( void ) const { return(m_szFileName); } //----------------------------------------------------------------------------- // Purpose: // Input : fd - // ulFileID - // bLoad - // pszName - // Output : Returns TRUE on success, FALSE on failure. //----------------------------------------------------------------------------- BOOL CWADTexture::Init(int fd, DWORD ulFileID, BOOL bLoad, LPCTSTR pszName) { // // Load header and copy needed data into members variables. // GRAPHICSFILESTRUCT FileInfo; bool bFound = g_Textures.FindGraphicsFile(&FileInfo, ulFileID); if (!bFound) { miptex_t hdr; _read(fd, (char *)&hdr, sizeof(hdr)); m_nWidth = hdr.width; m_nHeight = hdr.height; } else if (FileInfo.format == tfWAD3) { WAD3miptex_t hdr; _read(fd, (char *)&hdr, sizeof(hdr)); m_nWidth = hdr.width; m_nHeight = hdr.height; if (m_nHeight < 0) { return(FALSE); } } else { return(FALSE); } m_ulFileID = ulFileID; strcpy(m_szName, pszName); if (bFound) { strcpy(m_szFileName, FileInfo.filename); } if (m_nWidth * m_nHeight > MAX_TEXTURESIZE) { return(FALSE); } if (!m_szName[0]) { return(FALSE); } // set offset m_ulFileOffset = _tell(fd); if (bLoad) { return(Load()); } return(TRUE); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : CPalette * //----------------------------------------------------------------------------- CPalette *CWADTexture::GetPalette(void) const { static CPalette pal; pal.DeleteObject(); pal.CreatePalette(m_pPalette); return &pal; } //----------------------------------------------------------------------------- // Purpose: Returns a string of comma delimited keywords associated with this // material. // Input : pszKeywords - Buffer to receive keywords, NULL to query string length. // Output : Returns the number of characters in the keyword string. //----------------------------------------------------------------------------- int CWADTexture::GetKeywords(char *pszKeywords) const { // // Set the keywords to the WAD file name. // if (pszKeywords != NULL) { const char *pszLastSlash = strrchr(m_szFileName, '\\'); if (pszLastSlash != NULL) { pszLastSlash++; strcpy(pszKeywords, pszLastSlash); } else { strcpy(pszKeywords, m_szFileName); } } return(strlen(m_szFileName)); } //----------------------------------------------------------------------------- // Purpose: // Input : *pszName - // Output : Returns the length of the short name in characters. //----------------------------------------------------------------------------- int CWADTexture::GetShortName(char *pszName) const { char szBuf[MAX_PATH]; szBuf[0] = '\0'; if (pszName == NULL) { pszName = szBuf; } if (format == tfWAL) { const char *pszCopy = strstr(m_szName, "textures"); if (pszCopy == NULL) { pszCopy = m_szName; } else { pszCopy += strlen("textures\\"); } strcpy(pszName, pszCopy); // remove extension char *psz = strstr(szBuf, ".wal"); if (psz != NULL) { *psz = 0; } } else { strcpy(pszName, m_szName); } return(strlen(pszName)); } //----------------------------------------------------------------------------- // Purpose: Resizes a texture to be even powers of 2 in width and height. // Input : pLoadBuf - // Output : Returns TRUE on success, FALSE on failure. //----------------------------------------------------------------------------- BOOL CWADTexture::AdjustTexture(char *pLoadBuf) { // make height/width power of two int i, i2; for (i = 0; ; i++) { i2 = 1 << i; if (i2 >= m_nWidth) { m_datawidth = i2; break; } } for (i = 0; ; i++) { i2 = 1 << i; if (i2 >= m_nHeight) { m_dataheight = i2; break; } } // allocate data m_pData = _GraphicCacheAllocate(m_datawidth * m_dataheight); if (m_pData == NULL) { CString errmsg; errmsg.Format(IDS_ERRLOADGRAPHIC, m_szName); AfxMessageBox(errmsg); return FALSE; } // scale up to data ScaleBitmap(CSize(m_nWidth, m_nHeight), CSize(m_datawidth, m_dataheight), pLoadBuf, (char *)m_pData); return TRUE; } bool CWADTexture::IsLoaded() const { return (m_pData != NULL); } //----------------------------------------------------------------------------- // Purpose: Load data from file associated with m_ulFileID. // Input : fd - // hFile - // Output : Returns TRUE on success, FALSE on failure. //----------------------------------------------------------------------------- BOOL CWADTexture::Load(int fd, HANDLE hFile) { if (m_pData) { return TRUE; // already loaded } // if fd is -1, get it from base file.. otherwise we've been // given an fd by caller, so use that in loading GRAPHICSFILESTRUCT fileInfo; Q_memset( &fileInfo, 0, sizeof(fileInfo)); if (fd == -1) { // find graphics file - different loading based on wad type. if (!g_Textures.FindGraphicsFile(&fileInfo, m_ulFileID)) { return(FALSE); } // keep fd fd = fileInfo.fd; // seek to offset _lseek(fd, m_ulFileOffset, SEEK_SET); } m_bLocalPalette = FALSE; // dvs: if fd != -1, using FileInfo without initializing it!! if (!AllocateLoadBuffer(m_nWidth * m_nHeight)) { AfxMessageBox("Couldn't allocate a texture loading buffer."); return FALSE; } // load bitmap _read(fd, g_pLoadBuf, m_nWidth * m_nHeight); // // If WAD3, read the palette. // if (fileInfo.format == tfWAD3) { WORD nPal; _lseek(fd, (m_nWidth / 2 * m_nHeight / 2) + (m_nWidth / 4 * m_nHeight / 4) + (m_nWidth / 8 * m_nHeight / 8), SEEK_CUR); _read(fd, &nPal, sizeof nPal); Assert(nPal <= 256); if ((nPal > 0) && (nPal < 1024)) { m_bLocalPalette = TRUE; // setup palette m_pPalette = (LOGPALETTE *)malloc(sizeof(WORD) * 2 + sizeof(PALETTEENTRY) * nPal); // fast load - throw into buffer static unsigned char PalBuf[3 * 1024]; _read(fd, PalBuf, nPal * 3); // convert to LOGPALETTE for (int i = 0; i < nPal; i++) { m_pPalette->palPalEntry[i].peRed = PalBuf[i*3]; m_pPalette->palPalEntry[i].peGreen = PalBuf[i*3+1]; m_pPalette->palPalEntry[i].peBlue = PalBuf[i*3+2]; m_pPalette->palPalEntry[i].peFlags = D3DRMPALETTE_READONLY | PC_NOCOLLAPSE; } m_pPalette->palVersion = 0x300; m_pPalette->palNumEntries = nPal; } } AdjustTexture(g_pLoadBuf); return TRUE; } //----------------------------------------------------------------------------- // Purpose: If the buffer pointer passed in is not NULL, copies the image data // in RGB format to the buffer // Input : pImageRGB - Pointer to buffer that receives the image data. If the // pointer is NULL, no data is copied, only the data size is returned. // Output : Returns a the size of the RGB image in bytes. //----------------------------------------------------------------------------- int CWADTexture::GetImageDataRGB( void *pImageRGB ) { if ( pImageRGB != NULL ) { Load(); unsigned char *puchImage = ( unsigned char * )m_pData; unsigned char *pIndex = ( unsigned char * )pImageRGB; for (int y = 0; y < m_dataheight; y++) { for (int x = 0; x < m_datawidth; x++) { unsigned char chPaletteEntry = puchImage[y * m_datawidth + x]; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peRed; pIndex++; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peGreen; pIndex++; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peBlue; pIndex++; } } } return( m_datawidth * m_dataheight * 3 ); } //----------------------------------------------------------------------------- // Purpose: If the buffer pointer passed in is not NULL, copies the image data // in RGBA format to the buffer // Input : pImageRGBA - Pointer to buffer that receives the image data. If the // pointer is NULL, no data is copied, only the data size is returned. // Output : Returns a the size of the RGBA image in bytes. //----------------------------------------------------------------------------- int CWADTexture::GetImageDataRGBA( void *pImageRGBA ) { if ( pImageRGBA != NULL ) { unsigned char *puchImage = (unsigned char *)m_pData; unsigned char *pIndex = (unsigned char *)pImageRGBA; for (int y = 0; y < m_dataheight; y++) { for (int x = 0; x < m_datawidth; x++) { unsigned char chPaletteEntry = puchImage[y * m_datawidth + x]; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peRed; pIndex++; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peGreen; pIndex++; *pIndex = m_pPalette->palPalEntry[chPaletteEntry].peBlue; pIndex++; *pIndex = 0; pIndex++; } } } return( m_datawidth * m_dataheight * 4 ); } //----------------------------------------------------------------------------- // Purpose: // Input : size - //----------------------------------------------------------------------------- void CWADTexture::GetSize(SIZE& size) { size.cx = m_nWidth; size.cy = m_nHeight; } //----------------------------------------------------------------------------- // Purpose: Draws white "No Image" text in a black rectangle. // Input : pDC - // rect - // iFontHeight - // dwFlags - //----------------------------------------------------------------------------- void CWADTexture::DrawNoImage(CDC *pDC, RECT& rect, int iFontHeight) { // draw "no data" CFont *pOldFont = (CFont*) pDC->SelectStockObject(ANSI_VAR_FONT); COLORREF cr = pDC->SetTextColor(RGB(0xff, 0xff, 0xff)); COLORREF cr2 = pDC->SetBkColor(RGB(0, 0, 0)); // draw black rect first pDC->FillRect(&rect, CBrush::FromHandle(HBRUSH(GetStockObject(BLACK_BRUSH)))); // then text pDC->TextOut(rect.left+2, rect.top+2, "No Image", 8); pDC->SelectObject(pOldFont); pDC->SetTextColor(cr); pDC->SetBkColor(cr2); } //----------------------------------------------------------------------------- // Purpose: // Input : *pDC - // rect - // iFontHeight - // dwFlags - //----------------------------------------------------------------------------- void CWADTexture::Draw(CDC *pDC, RECT& rect, int iFontHeight, int iIconHeight, DrawTexData_t &DrawTexData) { if (!m_nWidth) { DrawNoImage(pDC, rect, iFontHeight); return; } // no data - if (!m_pData) { // try to load - if (!Load()) { DrawNoImage(pDC, rect, iFontHeight); return; } } static struct { BITMAPINFOHEADER bmih; unsigned short colorindex[256]; } bmi; BITMAPINFOHEADER& bmih = bmi.bmih; memset(&bmih, 0, sizeof bmih); bmih.biSize = sizeof(bmih); bmih.biWidth = m_datawidth; bmih.biHeight = -m_dataheight; // top-down DIB bmih.biCompression = BI_RGB; bmih.biBitCount = 8; bmih.biPlanes = 1; static BOOL bInit = FALSE; if (!bInit) { bInit = TRUE; for (int i = 0; i < 256; i++) { bmi.colorindex[i] = i; } } int dest_width = rect.right - rect.left; int dest_height = rect.bottom - rect.top; if (DrawTexData.nFlags & drawCaption) { dest_height -= iFontHeight + 4; } if (!(DrawTexData.nFlags & drawResizeAlways)) { if (m_nWidth < dest_width) { dest_width = m_nWidth; } if (m_nHeight < dest_height) { dest_height = m_nHeight; } } SetStretchBltMode(pDC->m_hDC, COLORONCOLOR); if (StretchDIBits(pDC->m_hDC, rect.left, rect.top, dest_width, dest_height, 0, 0, m_datawidth, m_dataheight, m_pData, (BITMAPINFO*)&bmi, DIB_PAL_COLORS, SRCCOPY) == GDI_ERROR) { Msg(mwError, "CWADTexture::Draw(): StretchDIBits failed."); } // // Caption. // if (DrawTexData.nFlags & drawCaption) { // draw background for name CBrush brCaption(RGB(0, 0, 255)); CRect rcCaption(rect); rcCaption.top = rcCaption.bottom - (iFontHeight + 5); pDC->FillRect(rcCaption, &brCaption); // draw name char szShortName[MAX_PATH]; int iLen = GetShortName(szShortName); pDC->TextOut(rect.left, rect.bottom - (iFontHeight + 4), szShortName, iLen); } } //----------------------------------------------------------------------------- // Purpose: Frees the static load buffer. //----------------------------------------------------------------------------- bool CWADTexture::Initialize(void) { return(AllocateLoadBuffer(g_nLoadSize)); } //----------------------------------------------------------------------------- // Purpose: Loads this texture from disk, if it is not already loaded. // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWADTexture::Load( void ) { if (m_pData != NULL) { // Already loaded. return(true); } return(Load(-1, NULL) == TRUE); } //----------------------------------------------------------------------------- // Purpose: Frees the static load buffer. //----------------------------------------------------------------------------- void CWADTexture::ShutDown(void) { if (g_pLoadBuf != NULL) { delete[] g_pLoadBuf; g_pLoadBuf = NULL; } }