|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implementation of the IEditorTexture interface for WAD textures.
//
//=============================================================================//
#include "stdafx.h"
#include <process.h>
#include <afxtempl.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#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 <tier0/memdbgon.h>
#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];
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; } }
|