//
// SaveSurfaceToFile.cpp
//
// Copyright (c) 2001 David Galeano
//
// Permission to use, copy, modify and distribute this software
// is hereby granted, provided that both the copyright notice and 
// this permission notice appear in all copies of the software, 
// derivative works or modified versions.
//
// THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
// CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES 
// WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
//

#define DIRECT3D_VERSION	0x0800

#include <d3d8.h>

#include <stdio.h>

//-----------------------------------------------------------------------------
//
// SaveSurfaceToTGAFile
//
//-----------------------------------------------------------------------------
HRESULT SaveSurfaceToTGAFile(LPDIRECT3DDEVICE8 pD3DDevice, const char *szFileName, LPDIRECT3DSURFACE8 pSurface)
{
	HRESULT hr=S_OK;

	D3DSURFACE_DESC d3dsd;
	LPDIRECT3DSURFACE8 pSurfaceCopy;

	pSurface->GetDesc(&d3dsd);

	hr=pD3DDevice->CreateImageSurface(d3dsd.Width, d3dsd.Height, d3dsd.Format, &pSurfaceCopy);

	if(SUCCEEDED(hr))
	{
		hr=pD3DDevice->CopyRects(pSurface, NULL, 0, pSurfaceCopy, NULL);

		// save the copy to the file
		D3DLOCKED_RECT d3dLR;
		if(SUCCEEDED(hr = pSurfaceCopy->LockRect(&d3dLR, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
		{
			// open file
			FILE *pFile=fopen(szFileName, "wb");
			if(pFile!=NULL)
			{
				setvbuf(pFile, NULL, _IOFBF, 64*1024);

				// write header
				char targaheader[18];

				// Set unused fields of header to 0
				memset(targaheader, 0, sizeof(targaheader));

				targaheader[2] = 2;	/* image type = uncompressed RGB */
				targaheader[12] = (char) (d3dsd.Width & 0xFF);
				targaheader[13] = (char) (d3dsd.Width >> 8);
				targaheader[14] = (char) (d3dsd.Height & 0xFF);
				targaheader[15] = (char) (d3dsd.Height >> 8);
				targaheader[16] = (d3dsd.Format==D3DFMT_A8R8G8B8 || d3dsd.Format==D3DFMT_A1R5G5B5 ? 32 : 24);
				targaheader[17] = 0x20;	/* Top-down, non-interlaced */

				fwrite(targaheader, 18, 1, pFile);

				// write file
				const BYTE *pImage=(const BYTE *)d3dLR.pBits;
				for(DWORD y=0; y<d3dsd.Height; y++)
				{
					if(d3dsd.Format==D3DFMT_X8R8G8B8)
					{
						const BYTE *pImageAux=pImage;
						for(DWORD x=0; x<d3dsd.Width; x++)
						{
							putc(pImageAux[0], pFile);
							putc(pImageAux[1], pFile);
							putc(pImageAux[2], pFile);
							pImageAux+=4;
						}
					}
					else if(d3dsd.Format==D3DFMT_A8R8G8B8)
					{
						fwrite(pImage, d3dsd.Width, 4, pFile);
					}
					else if(d3dsd.Format==D3DFMT_R5G6B5)
					{
						const BYTE *pImageAux=pImage;
						for(DWORD x=0; x<d3dsd.Width; x++)
						{
							const DWORD dwColor=*((WORD *)pImageAux);

							BYTE color;

							color=BYTE((dwColor&0x001f)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x7e0)>>5)<<2);
							putc(color, pFile);

							color=BYTE(((dwColor&0xf800)>>11)<<3);
							putc(color, pFile);

							pImageAux+=2;
						}
					}
					else if(d3dsd.Format==D3DFMT_X1R5G5B5)
					{
						const BYTE *pImageAux=pImage;
						for(DWORD x=0; x<d3dsd.Width; x++)
						{
							const DWORD dwColor=*((WORD *)pImageAux);

							BYTE color;

							color=BYTE((dwColor&0x001f)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x3e0)>>5)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x7c00)>>10)<<3);
							putc(color, pFile);

							pImageAux+=2;
						}
					}
					else if(d3dsd.Format==D3DFMT_A1R5G5B5)
					{
						const BYTE *pImageAux=pImage;
						for(DWORD x=0; x<d3dsd.Width; x++)
						{
							const DWORD dwColor=*((WORD *)pImageAux);

							BYTE color;

							color=BYTE((dwColor&0x001f)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x3e0)>>5)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x7c00)>>10)<<3);
							putc(color, pFile);

							color=BYTE(((dwColor&0x8000)>>15)*255);
							putc(color, pFile);

							pImageAux+=2;
						}
					}

					pImage+=d3dLR.Pitch;
				}

				fclose(pFile);
			}

		    pSurfaceCopy->UnlockRect();
		}

		pSurfaceCopy->Release();
	}

	return hr;
}

//-----------------------------------------------------------------------------
//
// SaveSurfaceToGrayscaleTGAFile
//
//-----------------------------------------------------------------------------
HRESULT SaveSurfaceToGrayscaleTGAFile(LPDIRECT3DDEVICE8 pD3DDevice, const char *szFileName, LPDIRECT3DSURFACE8 pSurface)
{
	HRESULT hr=S_OK;

	LPDIRECT3DSURFACE8 pSurfaceCopy=NULL;

	D3DSURFACE_DESC d3dsd;

	pSurface->GetDesc(&d3dsd);

	if(d3dsd.Pool!=D3DPOOL_SYSTEMMEM)
	{
		hr=pD3DDevice->CreateImageSurface(d3dsd.Width, d3dsd.Height, d3dsd.Format, &pSurfaceCopy);

		if(SUCCEEDED(hr))
		{
			hr=pD3DDevice->CopyRects(pSurface, NULL, 0, pSurfaceCopy, NULL);
		}
	}
	else
	{
		pSurfaceCopy=pSurface;
	}

	if(SUCCEEDED(hr))
	{
		// save the copy to the file
		D3DLOCKED_RECT d3dLR;
		if(SUCCEEDED(hr = pSurfaceCopy->LockRect(&d3dLR, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
		{
			// open file
			FILE *pFile=fopen(szFileName, "wb");
			if(pFile!=NULL)
			{
				setvbuf(pFile, NULL, _IOFBF, 64*1024);

				// write header
				char targaheader[18];

				// Set unused fields of header to 0
				memset(targaheader, 0, sizeof(targaheader));

				targaheader[2] = 3;		/* image type = uncompressed gray-scale */
				targaheader[12] = (char) (d3dsd.Width & 0xFF);
				targaheader[13] = (char) (d3dsd.Width >> 8);
				targaheader[14] = (char) (d3dsd.Height & 0xFF);
				targaheader[15] = (char) (d3dsd.Height >> 8);
				targaheader[16] = 8;
				targaheader[17] = 0x20;	/* Top-down, non-interlaced */

				fwrite(targaheader, 18, 1, pFile);

				// write file
				const BYTE *pImage=(const BYTE *)d3dLR.pBits;
				for(DWORD y=0; y<d3dsd.Height; y++)
				{
					const BYTE *pImageAux=pImage;
					for(DWORD x=0; x<d3dsd.Width; x++)
					{
						if(d3dsd.Format==D3DFMT_X8R8G8B8 || d3dsd.Format==D3DFMT_A8R8G8B8)
						{
							// save average greyscale
							putc((pImageAux[0]+pImageAux[1]+pImageAux[2])/3, pFile);
							pImageAux+=4;
						}
						else if(d3dsd.Format==D3DFMT_R5G6B5)
						{
							const DWORD dwColor=*((WORD *)pImageAux);

							DWORD colorAverage;

							colorAverage=((((dwColor&0xf800)>>11)<<3|0x07));
							colorAverage+=((((dwColor&0x7e0)>>5)<<2)|0x03);
							colorAverage+=(((dwColor&0x001f)<<3)|0x07);
							putc(colorAverage/3, pFile);

							pImageAux+=2;
						}
						else if(d3dsd.Format==D3DFMT_X1R5G5B5)
						{
							// save only green chanel
							const DWORD dwColor=*((WORD *)pImageAux);

							DWORD colorAverage;

							colorAverage=((((dwColor&0x7c00)>>10)<<3)|0x07);
							colorAverage+=((((dwColor&0x3e0)>>5)<<3)|0x07);
							colorAverage+=(((dwColor&0x001f)<<3)|0x07);
							putc(colorAverage/3, pFile);

							pImageAux+=2;
						}					
					}
					pImage+=d3dLR.Pitch;
				}

				fclose(pFile);
			}

		    pSurfaceCopy->UnlockRect();
		}
	}

	if(d3dsd.Pool!=D3DPOOL_SYSTEMMEM)
	{
		if(pSurfaceCopy!=NULL)
		{
			pSurfaceCopy->Release();
		}
	}

	return hr;
}

//-----------------------------------------------------------------------------
//
// CaptureScreenToFile
//
//-----------------------------------------------------------------------------
HRESULT CaptureScreenToFile(LPDIRECT3DDEVICE8 pD3DDevice)
{
	HRESULT hr=S_OK;

	LPDIRECT3DSURFACE8 pBackBuffer;

	if(SUCCEEDED(hr = pD3DDevice->GetRenderTarget(&pBackBuffer)))
	{
		char szName[MAX_PATH];

		// find a free filename
		DWORD nIndFile=0;
		do
		{
			sprintf(szName, "screen%02u.tga", nIndFile);
			nIndFile++;
		}
		while(GetFileAttributes(szName)!=DWORD(-1));

		SaveSurfaceToTGAFile(pD3DDevice, szName, pBackBuffer);

		pBackBuffer->Release();
	}

	return hr;
}
