source-engine/hammer/wadtexture.cpp

721 lines
16 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright 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];
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;
}
}