#include "pch.hpp"
//===========================================================================//
// File:	 Loader_TGA.cpp													 //
// Project:  GameOS					                                         //
// Contents: Used to load textures from .TGA files							 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 12/18/98  AG     Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Texture Manager.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "MemoryManager.hpp"


// definition of a TGA Header and related defines
#pragma pack(push,1)
typedef struct
{
    BYTE   m_idLength;			// Image ID Field Length
    BYTE   m_cMapType;			// Color Map Type
    BYTE   m_imageType;			// Image Type

    WORD   m_cMapIndex;			// First Entry Index
    WORD   m_cMapLength;		// Color Map Length
    BYTE   m_cMapEntrySize;		// Color Map Entry Size

    WORD   m_xOrigin;			// X-origin of Image
    WORD   m_yOrigin;			// Y-origin of Image
    WORD   m_imageWidth;		// Image Width
    WORD   m_imageHeight;		// Image Height
    BYTE   m_pixelDepth;		// Pixel Depth
    BYTE   m_imageDesc;			// Image Descriptor
} TGAHEADER;
#pragma pack(pop)

// Definitions for image types.
#define TGA_Null 0
#define TGA_Map 1
#define TGA_RGB 2
#define TGA_Mono 3
#define TGA_RLEMap 9
#define TGA_RLERGB 10
#define TGA_RLEMono 11
#define TGA_CompMap 32
#define TGA_CompMap4 33


//
// Returns true if the TGA is valid.
//
bool __stdcall gos_CheckValidTGA( BYTE *Data, DWORD DataSize )
{
	TGAHEADER TGAHeader;
	if( DataSize < sizeof(TGAHeader) )
		return false;
//
// Only 32 or 24 bit, not indexed files supported
//
	memcpy( &TGAHeader, Data, sizeof(TGAHeader) );
	if(!((TGAHeader.m_pixelDepth==32 ||
		TGAHeader.m_pixelDepth==24 ||
		(TGAHeader.m_pixelDepth==8 && TGAHeader.m_cMapLength<=256 && TGAHeader.m_cMapEntrySize==24 )) &&
		(TGAHeader.m_imageType==TGA_RGB || TGAHeader.m_imageType==TGA_RLERGB || TGAHeader.m_imageType==TGA_Map || TGAHeader.m_imageType==TGA_RLEMap) ))
	{
		return false;
	}
	if( TGAHeader.m_xOrigin!=0 || TGAHeader.m_yOrigin!=0 )
	{
		return false;
	}
//
// Support for rectangular textures (Always show in debug console)
//
	DWORD Width=TGAHeader.m_imageWidth;
	DWORD Height=TGAHeader.m_imageHeight;
	if( ((Width|Height)&(15|~2047L)) || (Width&(Width-1)) || (Height&(Height-1)) )
		return false;
//
// Otherwise valid texture
//
	return true;
}


void __stdcall DecodeTGADimensions( const char* FileName, BYTE *Data, DWORD DataSize, DWORD* pTextureWidth, DWORD* pTextureHeight )
{
	LOG_BLOCK("Gos3D::Texture.DecodeTGADimensions");
	*pTextureWidth = ((TGAHEADER *)Data)->m_imageWidth;
	*pTextureHeight = ((TGAHEADER *)Data)->m_imageHeight;
}




//
// Read in a TGA file and return a 32bit memory surface containing it.
//
// pBitmap contains the TGA file
// Size is the length of the TGA file
//
// Returns an IDirectDrawSurface7 for textures or a pointer to memory for non-textures
//
void* DecodeTGA( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf )
{
	LOG_BLOCK("Gos3D::Texture.DecodeTGA");
	IDirectDrawSurface7* DWSurface7=0;
	DDSURFACEDESC2 ddsd;
	TGAHEADER TGAHeader;
	DWORD Palette[256];
	int Pitch;
	DWORD* Dest;
	DWORD* Memory=0;
//
// Only 32 or 24 bit, not indexed files supported
//
	memcpy( &TGAHeader, Data, sizeof(TGAHeader) );
	if(!((TGAHeader.m_pixelDepth==32 ||
		TGAHeader.m_pixelDepth==24 ||
		(TGAHeader.m_pixelDepth==8 && TGAHeader.m_cMapLength<=256 && TGAHeader.m_cMapEntrySize==24 )) &&
		(TGAHeader.m_imageType==TGA_RGB || TGAHeader.m_imageType==TGA_RLERGB || TGAHeader.m_imageType==TGA_Map || TGAHeader.m_imageType==TGA_RLEMap) ))
	{
		STOP(( "Only 256 color palletized, 24 or 32 bit TGA files supported '%s' Depth=%d, MapLen=%d, EntrySize=%d, Type=%d",FileName, TGAHeader.m_pixelDepth, TGAHeader.m_cMapLength, TGAHeader.m_cMapEntrySize, TGAHeader.m_imageType  ));
	}
	if( TGAHeader.m_xOrigin!=0 ||
	    TGAHeader.m_yOrigin!=0 )
	{
		STOP(( "TGA file '%s' has an invalid offset",FileName ));
	}
//
// Get image information
//
	bool CompressedTGA=(TGAHeader.m_imageType==TGA_RLERGB || TGAHeader.m_imageType==TGA_RLEMap);
	int XReversed=TGAHeader.m_imageDesc&16;
	int YReversed=TGAHeader.m_imageDesc&32;
	DWORD Width=TGAHeader.m_imageWidth;
	DWORD Height=TGAHeader.m_imageHeight;
	DWORD XRepeat=1;
	DWORD YRepeat=1;
//
// Support for rectangular textures (Always show in debug console)
//
	if( TextureLoad )
	{
		if( ((Width|Height)&(15|~2047L)) || (Width&(Width-1)) || (Height&(Height-1)) )
			STOP(( "Only power of 2 textures up to 1024*1024 supported (%d,%d) '%s'",TGAHeader.m_imageWidth,TGAHeader.m_imageHeight,FileName ));
//
// Setup format of the DWORD version of the system memory image
//
		if( pDestSurf )
		{
			DWSurface7 = (IDirectDrawSurface7*)pDestSurf;
		}
		else
		{
			memset( &ddsd, 0, sizeof(ddsd) );
			ddsd.dwSize								= sizeof(ddsd);
			ddsd.dwFlags							= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
			ddsd.ddsCaps.dwCaps						= DDSCAPS_SYSTEMMEMORY | DDSCAPS_TEXTURE;
			ddsd.dwWidth							= Width;
			ddsd.dwHeight							= Height;
			ddsd.ddpfPixelFormat.dwSize				= sizeof(DDPIXELFORMAT);
			ddsd.ddpfPixelFormat.dwFlags			= DDPF_RGB;
			ddsd.ddpfPixelFormat.dwRGBBitCount		= 32;
			ddsd.ddpfPixelFormat.dwRGBAlphaBitMask	= 0xff000000;
			ddsd.ddpfPixelFormat.dwRBitMask			= 0x00ff0000;
			ddsd.ddpfPixelFormat.dwGBitMask			= 0x0000ff00;
			ddsd.ddpfPixelFormat.dwBBitMask			= 0x000000ff;
//
// Create system memory surface
//
			wCreateSurface( DDobject, &ddsd, &DWSurface7, NULL );
//
// Calculate memory useage
//
			gos_ChangeHeapSize( Heap_TextureOther, Width*Height*4, 1 );
		}
//
// Lock the surface and get pointer to bottom left (TGA's are stored upside down)
//
		ddsd.dwSize = sizeof(ddsd);
		wLock( DWSurface7, NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
		Pitch=ddsd.lPitch>>2;
		Dest=(DWORD*)ddsd.lpSurface+Pitch*(Height-1);
	}
	else
//
// Loading into a memory surface
//
	{
		Memory=Dest=(DWORD*)gos_Malloc(Width*Height*4, Heap_TextureOther);
		Pitch=Width;
		Dest+=Pitch*(Height-1);
	}
//
// Mirrored TGA?
//
	int XPitch=1;
	if( XReversed )
	{
		Dest+=Width-1;
		XPitch=-1;
	}
	if( YReversed )
	{
		Dest-=Pitch*(Height-1);
	}
	else
	{
		Pitch=-Pitch;
	}
//
// If using a palette, read it in convert it to a 768 byte palette
//
	BYTE* Source=(BYTE*)Data+TGAHeader.m_idLength+sizeof(TGAHEADER);
	bool UsePalette=TGAHeader.m_cMapLength>0;
	if( UsePalette )
	{
		for( int t0=0; t0<TGAHeader.m_cMapLength; t0++ )
		{
			Palette[t0]=((*(Source+2))<<16) + ((*(Source+1))<<8) + *Source + 0xff000000;
			Source+=3;
		}
	}
//
// Setup pixel reading and writing, shifts and masks
//
	BYTE RunCount=0,CopyCount=0;
	BYTE ControlByte=0;
	DWORD Pixel=0;
//
// Look for common case
//
	if( !CompressedTGA && XRepeat==1 && YRepeat==1 && !UsePalette )
	{
//
// Now decode TGA into system memory texture surface (Faster versions)
//
		if( TGAHeader.m_pixelDepth==24 )
		{
			for( int y=0; y<TGAHeader.m_imageHeight; y++ )
			{
				DWORD* tempDest=Dest;
				for( int x=0; x<TGAHeader.m_imageWidth; x++ )
				{
					DWORD Pixel=*Source + ((*(Source+1))<<8) + ((*(Source+2))<<16) + 0xff000000;
					Source+=3;
					*Dest=Pixel;
					Dest+=XPitch;
				}
				Dest=tempDest+Pitch;
			}
		}
		else
		{
			if( XPitch==1 )
			{
				for( int y=0; y<TGAHeader.m_imageHeight; y++ )
				{
					memcpy( Dest, Source, TGAHeader.m_imageWidth*4 );
					Source+=TGAHeader.m_imageWidth*4;
					Dest+=Pitch;
				}
			}
			else
			{
				for( int y=0; y<TGAHeader.m_imageHeight; y++ )
				{
					DWORD* tempDest=Dest;
					for( int x=0; x<TGAHeader.m_imageWidth; x++ )
					{
						*Dest=*(DWORD*)Source;
						Source+=4;
						Dest+=XPitch;
					}
					Dest=tempDest+Pitch;
				}
			}
		}
	}
	else
	{
//
// Now decode TGA into system memory texture surface (Generic version)
//
		for( int y=0; y<TGAHeader.m_imageHeight; y++ )
		{
			DWORD* tempDest=Dest;
			for( int x=0; x<TGAHeader.m_imageWidth; x++ )
			{
//
// Get source pixel (deal with RLE compression)
//
				if( CompressedTGA )
				{
					if( 0==RunCount )
					{
						if( 0==CopyCount )
						{
							ControlByte=*Source++;

							if( ControlByte >= 128 )
							{
								RunCount=(BYTE)(ControlByte-128);
							}
							else
							{
								CopyCount=ControlByte;
							}
						}
						else
						{
							CopyCount--;
						}
						
						if( UsePalette )
						{
							Pixel=Palette[*Source++];					
						}
						else
						{
							if( TGAHeader.m_pixelDepth==24 )
							{
								Pixel=*Source + ((*(Source+1))<<8) + ((*(Source+2))<<16) + 0xff000000;
								Source+=3;
							}
							else
							{
								Pixel=*(DWORD*)Source;
								Source+=4;
							}
						}
					}
					else
					{
						RunCount--;
					}
				}
				else
				{
					if( UsePalette )
					{
						Pixel=Palette[*Source++];					
					}
					else
					{
						if( TGAHeader.m_pixelDepth==24 )
						{
							Pixel=*Source + ((*(Source+1))<<8) + ((*(Source+2))<<16) + 0xff000000;
							Source+=3;
						}
						else
						{
							Pixel=*(DWORD*)Source;
							Source+=4;
						}
					}
				}

				for( DWORD xr=0; xr<XRepeat; xr++ )			// Non square texture X stretching
				{
					*Dest=Pixel;
					Dest+=XPitch;
				}
			}

			for( DWORD yr=1; yr<YRepeat; yr++ )
			{
				Dest=tempDest+Pitch;
				memcpy( Dest,tempDest,Width*4 );
				tempDest=Dest;
			}
			Dest=tempDest+Pitch;
		}
	}
//
//
//
	*TextureWidth=Width;
	*TextureHeight=Height;
//
// Loading into a texture?
//
	if( TextureLoad )
	{
		wUnlock( DWSurface7, NULL );
		return (void*)DWSurface7;
	}
	else
	{
		return (void*)Memory;
	}
}








