/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program 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.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : LevelEdit																	  *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/LevelEdit/TGAToDXT.cpp       $*
 *                                                                                             *
 *                       Author:: Ian Leslie			                                            *
 *                                                                                             *
 *                     $Modtime:: 8/29/01 5:35p                                               $*
 *                                                                                             *
 *                    $Revision:: 4                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "StdAfx.h"
#include "NvDXTLib.h"
#include "Targa.h"
#include "TGAToDXT.H"
#include <io.h>
#include	<stdlib.h>

// Singletons.
TGAToDXTClass _TGAToDXTConverter;


///////////////////////////////////////////////////////////////////////////////
//
//	TGAToDXTClass
//
///////////////////////////////////////////////////////////////////////////////
TGAToDXTClass::TGAToDXTClass()
	: WriteTimePtr (NULL),
	  BufferSize (1024),
	  BufferCount (0)
{
	Buffer = new unsigned char [BufferSize];
	ASSERT (Buffer != NULL);
}


///////////////////////////////////////////////////////////////////////////////
//
//	~TGAToDXTClass
//
///////////////////////////////////////////////////////////////////////////////
TGAToDXTClass::~TGAToDXTClass()
{
	// Clean-up.
	delete [] Buffer;
}


///////////////////////////////////////////////////////////////////////////////
//
//	Convert
//
///////////////////////////////////////////////////////////////////////////////
bool TGAToDXTClass::Convert (const char *inputpathname, const char *outputpathname, FILETIME *writetimeptr, bool &redundantalpha)
{
	bool  success;
	Targa targa;
	long	error;

	WriteTimePtr = writetimeptr;
	redundantalpha = false;

	success = false;
	error = targa.Load (inputpathname, TGAF_IMAGE, false);
	if (error == 0) {

		bool validbitdepth, validsize, validaspect;

		// Check that the targa is in the right format.
		// In order to be valid it must adhere to the following:
		// 1. Pixel depth must be 24 or 32 (compressor has no support for lower bit depths).
		// 2. Dimensions >= 4 (DDS block size is 4x4).
		// 3. Aspect ratio <= 1:8 (some H/W will not render textures above this ratio).
		// 4. Dimensions must be power of 2 (see below).
		validbitdepth = ((targa.Header.PixelDepth == 24) || (targa.Header.PixelDepth == 32));
		validsize	  = (targa.Header.Width >= 4) && (targa.Header.Height >= 4);
		validaspect	  = ((float) MAX (targa.Header.Width, targa.Header.Height)) / ((float) MIN (targa.Header.Width, targa.Header.Height)) <= 8.0f; 
		if (validbitdepth && validsize && validaspect) {
			
			unsigned char *byte;
			HRESULT			errorcode;

			targa.YFlip();

			// If TGA has an alpha channel...
			if (targa.Header.PixelDepth == 32) {
				
				// Analyse the alpha channel and ignore it if it contains redundant data (ie. is either all black or all white).
				byte = (unsigned char*) targa.GetImage();
				if ((*(byte + 3) == 0x00) || (*(byte + 3) == 0xff)) {

					const unsigned char alpha = *(byte + 3);

					redundantalpha = true;
					for (unsigned p = 0; p < ((unsigned) targa.Header.Width) * ((unsigned) targa.Header.Height); p++) {
						redundantalpha &= (*(byte + 3) == alpha);
						byte += 4;
					}
				}

				if (!redundantalpha) {

					errorcode = ::nvDXTcompress ((unsigned char*) targa.GetImage(), targa.Header.Width, targa.Header.Height, TF_DXT5, true, false, 4);

				} else {

					unsigned char *nonalphaimage, *nonalphabyte;

					// Remove the alpha channel and swizel the pixel data.
					nonalphaimage = new unsigned char [3 * ((unsigned) targa.Header.Width) * ((unsigned) targa.Header.Height)];
					nonalphabyte  = nonalphaimage;
					
					byte = (unsigned char*) targa.GetImage();
					for (unsigned p = 0; p < ((unsigned) targa.Header.Width) * ((unsigned) targa.Header.Height); p++) {

						*(nonalphabyte + 0) = *(byte + 0);
						*(nonalphabyte + 1) = *(byte + 1);
						*(nonalphabyte + 2) = *(byte + 2);
						nonalphabyte += 3;
						byte += 4;
					}

					errorcode = ::nvDXTcompress (nonalphaimage, targa.Header.Width, targa.Header.Height, TF_DXT1, true, false, 3);
					delete [] nonalphaimage;
				}

			} else {

				errorcode = ::nvDXTcompress ((unsigned char*) targa.GetImage(), targa.Header.Width, targa.Header.Height, TF_DXT1, true, false, 3);
			}

			// Was the image compressed successfully?
			// NOTE: Any image that does not have power of 2 dimensions will not be compressed.
			if (errorcode >= 0) {
				Write (outputpathname);
				success = true;
			}
		}
	}

	return (success);
}


///////////////////////////////////////////////////////////////////////////////
//
//	Write
//
///////////////////////////////////////////////////////////////////////////////
void TGAToDXTClass::Write (const char *outputpathname)
{
	HANDLE hfile;
	DWORD  bytecountwritten;
	
	hfile = ::CreateFile (outputpathname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0L, NULL);
	if (hfile != INVALID_HANDLE_VALUE) {
      LockFile (hfile, 0, 0, BufferCount, 0); 
      WriteFile (hfile, Buffer, BufferCount, &bytecountwritten, NULL);
      UnlockFile (hfile, 0, 0, BufferCount, 0); 
		
		// Stamp the write time (if one has been supplied).
		if (WriteTimePtr != NULL) {
			SetFileTime (hfile, NULL, NULL, WriteTimePtr);
		}

		CloseHandle (hfile);
	}

	// Reset buffer.
	BufferCount = 0;
}


///////////////////////////////////////////////////////////////////////////////
//
//	ReadDTXnFile
//
///////////////////////////////////////////////////////////////////////////////
void ReadDTXnFile (DWORD datacount, void *data)
{
	// Not implemented.
	ASSERT (false);
}


///////////////////////////////////////////////////////////////////////////////
//
//	WriteDTXnFile
//
///////////////////////////////////////////////////////////////////////////////
void WriteDTXnFile (DWORD datacount, void *data)
{
	// Ensure that the buffer is large enough.
	if (_TGAToDXTConverter.BufferSize < _TGAToDXTConverter.BufferCount + datacount) {

		unsigned			newbuffersize;
		unsigned char *newbuffer;

		newbuffersize = MAX (_TGAToDXTConverter.BufferSize * 2, _TGAToDXTConverter.BufferCount + datacount);
		newbuffer	  = new unsigned char [newbuffersize];
		ASSERT (newbuffer != NULL);
		memcpy (newbuffer, _TGAToDXTConverter.Buffer, _TGAToDXTConverter.BufferCount);
		delete [] _TGAToDXTConverter.Buffer;
		_TGAToDXTConverter.Buffer = newbuffer;
		_TGAToDXTConverter.BufferSize = newbuffersize;
	}

	// Write new data to buffer.
	memcpy (_TGAToDXTConverter.Buffer + _TGAToDXTConverter.BufferCount, data, datacount);
	_TGAToDXTConverter.BufferCount += datacount;
}

