/*
Copyright (C) 2002 Rice1964

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "stdafx.h"

#define ALLOW_USE_TEXTURE_FOR_CONSTANTS

static const uint8 sc_Mux32[32] = 
{
	MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
	MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_COMBINED|MUX_ALPHAREPLICATE,
	MUX_TEXEL0|MUX_ALPHAREPLICATE, MUX_TEXEL1|MUX_ALPHAREPLICATE, MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE,
	MUX_ENV|MUX_ALPHAREPLICATE, MUX_LODFRAC, MUX_PRIMLODFRAC, MUX_K5,		// Actually k5
	MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
	MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
	MUX_UNK, MUX_UNK, MUX_UNK, MUX_UNK,
	MUX_UNK, MUX_UNK, MUX_UNK, MUX_0
};

static const uint8 sc_Mux16[16] = 
{
	MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
	MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_COMBALPHA,
	MUX_TEXEL0|MUX_ALPHAREPLICATE, MUX_TEXEL1|MUX_ALPHAREPLICATE, MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE,
	MUX_ENV|MUX_ALPHAREPLICATE, MUX_LODFRAC, MUX_PRIMLODFRAC, MUX_0	
};
static const uint8 sc_Mux8[8] = 
{
	MUX_COMBINED, MUX_TEXEL0,   MUX_TEXEL1, MUX_PRIM,
	MUX_SHADE,    MUX_ENV,      MUX_1,      MUX_0
};


const char * translatedCombTypes[] =
{
	"0",
	"1",
	"COMBINED",
	"TEXEL0",
	"TEXEL1",
	"PRIM",
	"SHADE",
	"ENV",
	"COMBALPHA",
	"T0_ALPHA_wrong",
	"T1_ALPHA_wrong",
	"PRIM_ALPHA_wrong",
	"SHADE_ALPHA_wrong",
	"ENV_ALPHA_wrong",
	"LODFRAC",
	"PRIMLODFRAC",
	"K5",
	"UNK",
	"FACTOR_PRIM_MINUS_ENV",
	"FACTOR_ENV_MINUS_PRIM",
	"FACTOR_1_MINUS_PRIM",
	"FACTOR_0_MINUS_PRIM",
	"FACTOR_1_MINUS_ENV",
	"FACTOR_0_MINUS_ENV",
	"FACTOR_1_MINUS_PRIMALPHA",
	"FACTOR_1_MINUS_ENVALPHA",
	"FACTOR_HALF",
	"PRIM_X_PRIMALPHA",
	"1_MINUS_PRIM_X_ENV_PLUS_PRIM",
	"ENV_X_PRIM",
	"PRIM_X_1_MINUS_ENV",
	"PRIM_X_PRIM",
	"ENV_X_ENV",
};

char* muxTypeStrs[] = {
	"CM_FMT_TYPE_NOT_USED",
	"CM_FMT_TYPE1_D",
	"CM_FMT_TYPE2_A_ADD_D",
	"CM_FMT_TYPE3_A_MOD_C",
	"CM_FMT_TYPE4_A_SUB_B",
	"CM_FMT_TYPE5_A_MOD_C_ADD_D",
	"CM_FMT_TYPE6_A_LERP_B_C",
	"CM_FMT_TYPE7_A_SUB_B_ADD_D",
	"CM_FMT_TYPE8_A_SUB_B_MOD_C",
	"CM_FMT_TYPE9_A_B_C_D",
	"CM_FMT_TYPE_NOT_CHECKED",
};

void DecodedMux::Decode(uint32 dwMux0, uint32 dwMux1)
{
	m_dwMux0 = dwMux0;
	m_dwMux1 = dwMux1;

	aRGB0  = uint8((dwMux0>>20)&0x0F);	// c1 c1		// a0
	bRGB0  = uint8((dwMux1>>28)&0x0F);	// c1 c2		// b0
	cRGB0  = uint8((dwMux0>>15)&0x1F);	// c1 c3		// c0
	dRGB0  = uint8((dwMux1>>15)&0x07);	// c1 c4		// d0
	
	aA0    = uint8((dwMux0>>12)&0x07);	// c1 a1		// Aa0
	bA0    = uint8((dwMux1>>12)&0x07);	// c1 a2		// Ab0
	cA0    = uint8((dwMux0>>9 )&0x07);	// c1 a3		// Ac0
	dA0    = uint8((dwMux1>>9 )&0x07);	// c1 a4		// Ad0
	
	aRGB1  = uint8((dwMux0>>5 )&0x0F);	// c2 c1		// a1
	bRGB1  = uint8((dwMux1>>24)&0x0F);	// c2 c2		// b1
	cRGB1  = uint8((dwMux0    )&0x1F);	// c2 c3		// c1
	dRGB1  = uint8((dwMux1>>6 )&0x07);	// c2 c4		// d1
	
	aA1    = uint8((dwMux1>>21)&0x07);	// c2 a1		// Aa1
	bA1    = uint8((dwMux1>>3 )&0x07);	// c2 a2		// Ab1
	cA1    = uint8((dwMux1>>18)&0x07);	// c2 a3		// Ac1
	dA1    = uint8((dwMux1    )&0x07);	// c2 a4		// Ad1

	//This fuction will translate the decode mux info further, so we can use
	//the decode data better in D3D.
	//Will translate A,B,C,D to unified presentation
	aRGB0  = sc_Mux16[aRGB0];
	bRGB0  = sc_Mux16[bRGB0];
	cRGB0  = sc_Mux32[cRGB0];
	dRGB0  = sc_Mux8[dRGB0];
	
	aA0    = sc_Mux8[aA0];
	bA0    = sc_Mux8[bA0];
	cA0    = sc_Mux8[cA0];
	dA0    = sc_Mux8[dA0];
	
	aRGB1  = sc_Mux16[aRGB1];
	bRGB1  = sc_Mux16[bRGB1];
	cRGB1  = sc_Mux32[cRGB1];
	dRGB1  = sc_Mux8[dRGB1];
	
	aA1    = sc_Mux8[aA1];
	bA1    = sc_Mux8[bA1];
	cA1    = sc_Mux8[cA1];
	dA1    = sc_Mux8[dA1];

	m_bShadeIsUsed[1] = isUsedInAlphaChannel(MUX_SHADE);
	m_bShadeIsUsed[0] = isUsedInColorChannel(MUX_SHADE);
	m_bTexel0IsUsed = isUsed(MUX_TEXEL0);
	m_bTexel1IsUsed = isUsed(MUX_TEXEL1);

	m_dwShadeColorChannelFlag = 0;
	m_dwShadeAlphaChannelFlag = 0;
	m_ColorTextureFlag[0] = 0;
	m_ColorTextureFlag[1] = 0;
}

int DecodedMux::Count(uint8 val, int cycle, uint8 mask)
{
	uint8* pmux = m_bytes;
	int count=0;
	int start=0;
	int end=16;

	if( cycle >= 0 )
	{
		start = cycle*4;
		end = start+4;
	}


	for( int i=start; i<end; i++ )
	{
		if( (pmux[i]&mask) == (val&mask) )
		{
			count++;
		}
	}

	return count;
}


bool DecodedMux::isUsed(uint8 val, uint8 mask)
{
	uint8* pmux = m_bytes;
	bool isUsed = false;
	for( int i=0; i<16; i++ )
	{
		if( (pmux[i]&mask) == (val&mask) )
		{
			isUsed = true;
			break;
		}
	}

	return isUsed;
}

bool DecodedMux::isUsedInAlphaChannel(uint8 val, uint8 mask)
{
	uint8* pmux = m_bytes;
	bool isUsed = false;
	for( int i=0; i<16; i++ )
	{
		if( (i/4)%2 == 0 )
			continue;	//Don't test color channel

		if( (pmux[i]&mask) == (val&mask) )
		{
			isUsed = true;
			break;
		}
	}

	return isUsed;
}

bool DecodedMux::isUsedInColorChannel(uint8 val, uint8 mask)
{
	uint8* pmux = m_bytes;
	bool isUsed = false;
	for( int i=0; i<16; i++ )
	{
		if( (i/4)%2 == 0 && (pmux[i]&mask) == (val&mask) )
		{
			isUsed = true;
			break;
		}
	}

	return isUsed;
}


bool DecodedMux::isUsedInCycle(uint8 val, int cycle, CombineChannel channel, uint8 mask)
{
	cycle *=2;
	if( channel == ALPHA_CHANNEL ) cycle++;

	uint8* pmux = m_bytes;
	for( int i=0; i<4; i++ )
	{
		if( (pmux[i+cycle*4]&mask) == (val&mask) )
		{
			return true;
		}
	}

	return false;
}

bool DecodedMux::isUsedInCycle(uint8 val, int cycle, uint8 mask)
{
	return isUsedInCycle(val, cycle/2, cycle%2?ALPHA_CHANNEL:COLOR_CHANNEL, mask);
}


void DecodedMux::ConvertComplements()
{
	//For (A-B)*C+D, if A=1, then we can convert A-B to Ac-0
	if( aRGB0 != MUX_1 && bRGB0 != MUX_0 )
	{
		aRGB0 = bRGB0|MUX_COMPLEMENT;
		bRGB0 = MUX_0;
	}
	if( aRGB1 != MUX_1 && bRGB1 != MUX_0 )
	{
		aRGB1 = bRGB1|MUX_COMPLEMENT;
		bRGB1 = MUX_0;
	}
	if( aA0 != MUX_1 && bA0 != MUX_0 )
	{
		aA0 = bA0|MUX_COMPLEMENT;
		bA0 = MUX_0;
	}
	if( aA1 != MUX_1 && bA1 != MUX_0 )
	{
		aA1 = bA1|MUX_COMPLEMENT;
		bA1 = MUX_0;
	}
}


CombinerFormatType DecodedMux::GetCombinerFormatType(uint32 cycle)
{
	//Analyze the formula
	/*
	C=0					= D
	A==B				= D
	B=0, C=1, D=0		= A
	A=1, B=0, D=0		= C
	C=1, B==D			= A
	A=1, C=1, D=0		= 1-B
	D = 1				= 1
	*/
	return CM_FMT_TYPE_D;
}

void DecodedMuxForPixelShader::Simplify(void)
{
	CheckCombineInCycle1();
	//Reformat();

	if( g_curRomInfo.bTexture1Hack )
	{
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,2);
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,3);
	}
	splitType[0] = CM_FMT_TYPE_NOT_USED;
	splitType[1] = CM_FMT_TYPE_NOT_USED;
	splitType[2] = CM_FMT_TYPE_NOT_USED;
	splitType[3] = CM_FMT_TYPE_NOT_USED;
	mType = CM_FMT_TYPE_NOT_USED;

	m_bTexel0IsUsed = isUsed(MUX_TEXEL0);
	m_bTexel1IsUsed = isUsed(MUX_TEXEL1);
}

void DecodedMuxForSemiPixelShader::Reset(void)
{
	Decode(m_dwMux0, m_dwMux1);
	splitType[0] = CM_FMT_TYPE_NOT_CHECKED;
	splitType[1] = CM_FMT_TYPE_NOT_CHECKED;
	splitType[2] = CM_FMT_TYPE_NOT_CHECKED;
	splitType[3] = CM_FMT_TYPE_NOT_CHECKED;

	Hack();

	gRSP.bProcessDiffuseColor = false;
	gRSP.bProcessSpecularColor = false;

	CheckCombineInCycle1();
	if( g_curRomInfo.bTexture1Hack )
	{
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,2);
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,3);
	}

	m_bTexel0IsUsed = isUsed(MUX_TEXEL0);
	m_bTexel1IsUsed = isUsed(MUX_TEXEL1);
}

void DecodedMuxForOGL14V2::Simplify(void)
{
	CheckCombineInCycle1();
	if( g_curRomInfo.bTexture1Hack )
	{
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,2);
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,3);
	}
	Reformat();

	UseTextureForConstant();
	Reformat();

	m_bTexel0IsUsed = isUsed(MUX_TEXEL0);
	m_bTexel1IsUsed = isUsed(MUX_TEXEL1);
}

void DecodedMux::Simplify(void)
{
	CheckCombineInCycle1();
	if( gRDP.otherMode.text_lod )
		ConvertLODFracTo0();
	if( g_curRomInfo.bTexture1Hack )
	{
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,2);
		ReplaceVal(MUX_TEXEL1,MUX_TEXEL0,3);
	}
	Reformat();

	UseShadeForConstant();
	Reformat();

	if( m_dwShadeColorChannelFlag == MUX_0 )
	{
		MergeShadeWithConstants();
		Reformat();
	}

#ifdef ALLOW_USE_TEXTURE_FOR_CONSTANTS
	UseTextureForConstant();
	for( int i=0; i<2; i++ )
	{
		if( m_ColorTextureFlag[i] != 0 )
		{
			if( m_dwShadeColorChannelFlag == m_ColorTextureFlag[i] )
			{
				ReplaceVal(MUX_SHADE,MUX_TEXEL0+i,N64Cycle0RGB);
				ReplaceVal(MUX_SHADE,MUX_TEXEL0+i,N64Cycle1RGB);
				m_dwShadeColorChannelFlag = 0;
			}
			if( m_dwShadeAlphaChannelFlag == m_ColorTextureFlag[i] )
			{
				ReplaceVal(MUX_SHADE,MUX_TEXEL0+i,N64Cycle0Alpha);
				ReplaceVal(MUX_SHADE,MUX_TEXEL0+i,N64Cycle1Alpha);
				ReplaceVal(MUX_SHADE|MUX_ALPHAREPLICATE,(MUX_TEXEL0+i)|MUX_ALPHAREPLICATE,N64Cycle0RGB,MUX_MASK_WITH_ALPHA);
				ReplaceVal(MUX_SHADE|MUX_ALPHAREPLICATE,(MUX_TEXEL0+i)|MUX_ALPHAREPLICATE,N64Cycle1RGB,MUX_MASK_WITH_ALPHA);
				m_dwShadeAlphaChannelFlag = 0;
			}
		}
	}
	Reformat();
#endif

	m_bTexel0IsUsed = isUsed(MUX_TEXEL0);
	m_bTexel1IsUsed = isUsed(MUX_TEXEL1);
}

void DecodedMux::Reformat(bool do_complement)
{
	if( m_dWords[N64Cycle0RGB] == m_dWords[N64Cycle1RGB] )
	{
		aRGB1 = MUX_0;
		bRGB1 = MUX_0;
		cRGB1 = MUX_0;
		dRGB1 = MUX_COMBINED;
		splitType[N64Cycle1RGB] = CM_FMT_TYPE_NOT_USED;
	}

	if( m_dWords[N64Cycle0Alpha] == m_dWords[N64Cycle1Alpha] )
	{
		aA1 = MUX_0;
		bA1 = MUX_0;
		cA1 = MUX_0;
		dA1 = MUX_COMBINED;
		splitType[N64Cycle1Alpha] = CM_FMT_TYPE_NOT_USED;
	}

	for( int i=0; i<4; i++ )
	{
		if( splitType[i] == CM_FMT_TYPE_NOT_USED )
		{
			continue;	//Skip this, it is not used
		}

		N64CombinerType &m = m_n64Combiners[i];
		//if( m.a == MUX_0 || m.c == MUX_0 || m.a ==  m.b )	m.a = m.b = m.c = MUX_0;
		if( m.c == MUX_0 || m.a ==  m.b )	m.a = m.b = m.c = MUX_0;
		if( do_complement && (m.b == MUX_1 || m.d == MUX_1) )  m.a = m.b = m.c = MUX_0;
		if( m.a == MUX_0 && m.b == m.d ) 
		{
			m.a = m.b;
			m.b = m.d = 0;
			//Hack for Mario Tennis
			if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS && m.c == MUX_TEXEL1 )
			{
				if( do_complement )
					m.c = MUX_TEXEL0|MUX_COMPLEMENT;
				else
				{
					m.a = m.c;
					m.c = m.b;
					m.b = m.a;
					m.a = MUX_1;
				}
			}
			//m.c ^= MUX_COMPLEMENT;
		}

		//Type 1	== D
		//Analyze the formula
		//Check Type 1
		//D = 1				= D(=1)
		//C=0				= D
		//A==B				= D
		//B=0, C=1, D=0		= A
		//C=1, B==D			= A
		//A=1, B=0, D=0		= C
		//A=1, C=1, D=0		= 1-B

		splitType[i] = CM_FMT_TYPE_NOT_CHECKED;	//All Type 1 will be changed to = D
		if( m.c == MUX_0 || m.a==m.b || ( do_complement && (m.d == MUX_1 || m.b==MUX_1)) )
		{
			splitType[i] = CM_FMT_TYPE_D;	//All Type 1 will be changed to = D
			m.a = m.b = m.c = MUX_0;
			if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )	splitType[i] = CM_FMT_TYPE_NOT_USED;
		}
		else if( (m.b == MUX_0 && m.c == MUX_1 && m.d == MUX_0 ) || ( m.c == MUX_1 && m.b==m.d ) )
		{
			splitType[i] = CM_FMT_TYPE_D;	//All Type 1 will be changed to = D
			m.d = m.a;
			m.a =  m.b = m.c = MUX_0;
			if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )	splitType[i] = CM_FMT_TYPE_NOT_USED;
		}
		else if( m.a == MUX_1 && m.b == MUX_0 && m.d == MUX_0 )
		{
			splitType[i] = CM_FMT_TYPE_D;	//All Type 1 will be changed to = D
			m.d = m.c;
			m.a =  m.b = m.c = MUX_0;
			if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )	splitType[i] = CM_FMT_TYPE_NOT_USED;
		}
		else if( m.a == MUX_1 && m.c == MUX_1 && m.d == MUX_0 && do_complement )
		{
			splitType[i] = CM_FMT_TYPE_D;	//All Type 1 will be changed to = D
			m.d = m.b^MUX_COMPLEMENT;
			m.a =  m.b = m.c = MUX_0;
			if( m.d == MUX_COMBINED && i>=N64Cycle1RGB )	splitType[i] = CM_FMT_TYPE_NOT_USED;
		}

		if( splitType[i] == CM_FMT_TYPE_NOT_USED )
			continue;

		if( splitType[i] == CM_FMT_TYPE_D )
		{
			if( (i == N64Cycle0RGB || i == N64Cycle0Alpha) && splitType[i+2]!=CM_FMT_TYPE_NOT_USED )	//Cycle 1's Color or Alpha
			{
				uint8 saveD = m.d;
				for( int j=0; j<4; j++ )
				{
					if( (m_bytes[j+i*4+8]&MUX_MASK) == MUX_COMBINED )
					{
						m_bytes[j+i*4+8] = saveD|(m_bytes[j+i*4+8]&0xC0);	//Replace cycle's CMB with D from cycle 1
					}
				}
				m_dWords[i] = m_dWords[i+2];
				splitType[i+2]=CM_FMT_TYPE_NOT_USED;
				m_dWords[i+2] = 0x02000000;
				i=i-1;		// Throw the first cycle result away, use 2nd cycle for the 1st cycle
							// and then redo the 1st cycle
				continue;
			}

			if( (i==2 || i == 3) && (m.d&MUX_MASK) == MUX_COMBINED )
			{
				splitType[i] = CM_FMT_TYPE_NOT_USED;
			}
			continue;
		}


		//Type 2: A+D	' D3D's ADD
		//B=0, C=1			= A+D
		//A=1, B=0			= C+D
		splitType[i] = CM_FMT_TYPE_A_ADD_D;			//All Type 2 will be changed to = A+D
		if( m.b == MUX_0 && m.c == MUX_1 )
		{
			if( m.d == MUX_TEXEL0 || m.d == MUX_TEXEL1 )	swap(m.a, m.d);
			if( m.a == MUX_COMBINED ) swap(m.a, m.d);
			continue;
		}

		if( m.a == MUX_1 && m.b == MUX_0 )
		{
			m.a = m.c;			//Change format A+D
			m.c = MUX_1;
			if( m.d == MUX_TEXEL0 || m.d == MUX_TEXEL1 )	swap(m.a, m.d);
			continue;
		}


		//Type 3: A*C
		//B=0, D=0			= A*C
		//A=1, D=0			= (1-A)*C
		splitType[i] = CM_FMT_TYPE_A_MOD_C;			//A*C
		if( m.b == MUX_0 && m.d == MUX_0 )
		{
			if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )	swap(m.a, m.c);
			if( m.a == MUX_COMBINED ) swap(m.a, m.c);
			continue;
		}

		if( m.a == MUX_1 && m.d == MUX_0 && do_complement )
		{
			m.a = m.b^MUX_COMPLEMENT;
			m.b = MUX_0;
			if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )	swap(m.a, m.c);
			if( m.a == MUX_COMBINED ) swap(m.a, m.c);
			continue;
		}

		//Type 4: A-B	' D3D's SUB
		//C=1, D=0			= A-B
		splitType[i] = CM_FMT_TYPE_A_SUB_B;			//A-B
		if( m.c == MUX_1 && m.d == MUX_0 )
		{
			continue;
		}

		//Type 5: A*C+D	, D3D's MULTIPLYADD
		//B=0				= A*C+D
		//A=1				= (1-B) * C + D
		splitType[i] = CM_FMT_TYPE_A_MOD_C_ADD_D;
		if( m.b == MUX_0 )
		{
			if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )	swap(m.a, m.c);
			if( m.a == MUX_COMBINED ) swap(m.a, m.c); 
			continue;
		}

		if( m.a == MUX_1 && m.b!=m.d && do_complement )
		{
			m.a = m.b^MUX_COMPLEMENT;
			m.b = MUX_0;
			if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )	swap(m.a, m.c);
			if( m.a == MUX_COMBINED ) swap(m.a, m.c); 
			continue;
		}

		//Type 6: (A-B)*C+B	Map to D3D LERP, or BLENDALPHA
		//D==B
		splitType[i] = CM_FMT_TYPE_A_LERP_B_C;
		if( m.b == m.d )
		{
			continue;
		}


		//Type 7: A-B+D
		//C=1				= A-B+D
		splitType[i] = CM_FMT_TYPE_A_SUB_B_ADD_D;
		if( m.c == MUX_1 )
		{
			if( m.c == MUX_TEXEL0 || m.c == MUX_TEXEL1 )	swap(m.a, m.c);
			continue;
		}

		//Type 8: (A-B)*C
		splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
		if( m.d == MUX_0 )
		{
			continue;
		}

		if( m.c == m.d && do_complement )	// (A-B)*C+C   ==> (A + B|C ) * C
		{
			m.d = MUX_0;
			m.b |= MUX_COMPLEMENT;
			continue;
		}

		if( m.a == m.d )
		{
			splitType[i] = CM_FMT_TYPE_A_B_C_A;
			continue;
		}

		//Type 9: (A-B)*C+D
		splitType[i] = CM_FMT_TYPE_A_B_C_D;
	}

	if( (splitType[0] == CM_FMT_TYPE_D && splitType[2]!= CM_FMT_TYPE_NOT_USED ) ||	//Cycle 1 Color
		(isUsedInCycle(MUX_COMBINED,1,COLOR_CHANNEL) == false && isUsedInCycle(MUX_COMBINED,1,ALPHA_CHANNEL) == false && splitType[2]!= CM_FMT_TYPE_NOT_USED) )
	{
		//Replace cycle 1 color with cycle 2 color because we have already replace cycle2's cmb 
		aRGB0 = aRGB1;
		bRGB0 = bRGB1;
		cRGB0 = cRGB1;
		dRGB0 = dRGB1;
		aRGB1 = MUX_0;
		bRGB1 = MUX_0;
		cRGB1 = MUX_0;
		dRGB1 = MUX_COMBINED;
		splitType[0] = splitType[2];
		splitType[2] = CM_FMT_TYPE_NOT_USED;
	}

	if( (splitType[1] == CM_FMT_TYPE_D && splitType[3]!= CM_FMT_TYPE_NOT_USED ) ||	//Cycle 2 Alpha
		( isUsedInCycle(MUX_COMBINED,1,ALPHA_CHANNEL) == false && isUsedInCycle(MUX_COMBINED|MUX_ALPHAREPLICATE,1,COLOR_CHANNEL,MUX_MASK_WITH_ALPHA) == false && splitType[3]!= CM_FMT_TYPE_NOT_USED) )
	{
		//Replace cycle 1 alpha with cycle 2 alpha because we have already replace cycle2's cmb 
		aA0 = aA1;
		bA0 = bA1;
		cA0 = cA1;
		dA0 = dA1;
		aA1 = MUX_0;
		bA1 = MUX_0;
		cA1 = MUX_0;
		dA1 = MUX_COMBINED;
		splitType[1] = splitType[3];
		splitType[3] = CM_FMT_TYPE_NOT_USED;
	}

	if( splitType[0] == CM_FMT_TYPE_A_MOD_C && splitType[2] == CM_FMT_TYPE_A_ADD_D )
	{
		m_n64Combiners[0].d = (m_n64Combiners[2].a & MUX_MASK) == MUX_COMBINED ? m_n64Combiners[2].d : m_n64Combiners[2].a;
		splitType[0] = CM_FMT_TYPE_A_MOD_C_ADD_D;
		splitType[2] = CM_FMT_TYPE_NOT_USED;
		m_n64Combiners[2].a = MUX_0;
		m_n64Combiners[2].c = MUX_0;
		m_n64Combiners[2].d = MUX_COMBINED;
	}

	if( splitType[1] == CM_FMT_TYPE_A_MOD_C && splitType[3] == CM_FMT_TYPE_A_ADD_D )
	{
		m_n64Combiners[1].d = (m_n64Combiners[3].a & MUX_MASK) == MUX_COMBINED ? m_n64Combiners[3].d : m_n64Combiners[3].a;
		splitType[1] = CM_FMT_TYPE_A_MOD_C_ADD_D;
		splitType[3] = CM_FMT_TYPE_NOT_USED;
		m_n64Combiners[3].a = MUX_0;
		m_n64Combiners[3].c = MUX_0;
		m_n64Combiners[3].d = MUX_COMBINED;
	}
	
	mType = max(max(max(splitType[0], splitType[1]),splitType[2]),splitType[3]);
}

char* MuxGroupStr[4] =
{
	"Color0",
	"Alpha0",
	"Color1",
	"Alpha1",
};

char* DecodedMux::FormatStr(uint8 val, char *buf)
{
	if( val == CM_IGNORE_BYTE )
	{
		strcpy(buf," ");
	}
	else
	{
		strcpy(buf, translatedCombTypes[val&MUX_MASK]);
		if( val&MUX_ALPHAREPLICATE )
			strcat(buf,"|A");
		if( val&MUX_COMPLEMENT )
			strcat(buf,"|C");
		if( val&MUX_NEG )
			strcat(buf,"|N");
	}

	return buf;
}

void DecodedMux::Display(bool simplified,FILE *fp)
{
	DecodedMux decodedMux;
	DecodedMux *mux;
	if( simplified )
	{
		mux = this;
	}
	else
	{
		decodedMux.Decode(m_dwMux0, m_dwMux1);
		mux = &decodedMux;
	}

	char buf0[30];
	char buf1[30];
	char buf2[30];
	char buf3[30];

	for( int i=0; i<2; i++ )
	{
		for(int j=0;j<2;j++)
		{
			N64CombinerType &m = mux->m_n64Combiners[i+2*j];
			if( fp )
			{
				fprintf(fp,"%s: (%s - %s) * %s + %s\n", MuxGroupStr[i+2*j], FormatStr(m.a,buf0), 
					FormatStr(m.b,buf1), FormatStr(m.c,buf2), FormatStr(m.d,buf3));
			}
			else
			{
				DebuggerAppendMsg("%s: (%s - %s) * %s + %s\n", MuxGroupStr[i+2*j], FormatStr(m.a,buf0), 
					FormatStr(m.b,buf1), FormatStr(m.c,buf2), FormatStr(m.d,buf3));
			}
		}
	}
}

int DecodedMux::HowManyConstFactors()
{
	int n=0;
	if( isUsed(MUX_PRIM) ) n++;
	if( isUsed(MUX_ENV) ) n++;
	if( isUsed(MUX_LODFRAC) ) n++;
	if( isUsed(MUX_PRIMLODFRAC) ) n++;
	return n;
}

int DecodedMux::HowManyTextures()
{
	int n=0;
	if( isUsed(MUX_TEXEL0) ) n++;
	if( isUsed(MUX_TEXEL1) ) n++;
	return n;
}

int DecodedMux::CountTexels(void)
{
	int count=0;

	for( int i=0; i<4; i++ )
	{
		N64CombinerType &m = m_n64Combiners[i];
		count = max(count, ::CountTexel1Cycle(m));
		if( count == 2 ) 
			break;
	}

	return count;
}

void DecodedMux::ReplaceVal(uint8 val1, uint8 val2, int cycle, uint8 mask)
{
	int start = 0;
	int end = 16;

	if( cycle >= 0 )
	{
		start = cycle*4;
		end = start+4;
	}

	uint8* pmux = m_bytes;
	for( int i=start; i<end; i++ )
	{
		if( (pmux[i]&mask) == (val1&mask) )
		{
			pmux[i] &= (~mask);
			pmux[i] |= val2;
		}
	}
}

uint32 DecodedMux::GetCycle(int cycle, CombineChannel channel)
{
	uint32* pmux = m_dWords;
	if( channel == COLOR_CHANNEL )
	{
		return pmux[cycle*2];
	}
	else
	{
		return pmux[cycle*2+1];
	}

}

uint32 DecodedMux::GetCycle(int cycle)
{
	return m_dWords[cycle];
}

enum ShadeConstMergeType
{
	SHADE_DO_NOTHING,
	SHADE_ADD_PRIM,				// Shade+PRIM
	SHADE_ADD_ENV,				// Shade+ENV
	SHADE_ADD_PRIM_ALPHA,			// Shade+PRIM_ALPHA
	SHADE_ADD_ENV_ALPHA,			// Shade+ENV_ALPHA
	SHADE_MINUS_PRIM_PLUS_ENV,	
	SHADE_MINUS_ENV_PLUS_PRIM,
	SHADE_MOD_ENV,
};

typedef struct {
	uint64						mux;	// simplified
	ShadeConstMergeType		op;
} ShadeConstMergeMapType;


ShadeConstMergeMapType MergeShadeWithConstantsMaps[] =
{
	0,	SHADE_DO_NOTHING,
	0x0007000600070006,	SHADE_MOD_ENV,		// SHADE * ENV
};

// 0x05070501, 0x00070006		//(1 - PRIM) * ENV + PRIM
// 0x00050003, 0x00050003		//(TEXEL0 - 0) * PRIM + 0

void DecodedMux::MergeShadeWithConstants(void)
{
	// This function should be called afte the mux has been simplified
	// The goal of this function is to merge as many as possible constants with shade
	// so to reduce the totally number of constants to 0 or 1
	// And at the same time, to reduce the complexity of the whole mux
	// so we can implement the mux easiler when lower end video cards

	// We can only try to merge shade with constants for:
	// 1 cycle mode or 2 cycle mode and shade is not used in the 2nd cycle

	if( m_bShadeIsUsed[0] ) MergeShadeWithConstantsInChannel(COLOR_CHANNEL);
	if( m_bShadeIsUsed[1] ) MergeShadeWithConstantsInChannel(ALPHA_CHANNEL);
}

void DecodedMux::MergeShadeWithConstantsInChannel(CombineChannel channel)
{
	bool usedIn[2];
	uint32 cycleVal;
	int cycleNum;

	usedIn[0] = isUsedInCycle(MUX_SHADE,channel);
	usedIn[1] = isUsedInCycle(MUX_SHADE,channel+2);
	if( usedIn[0] && usedIn[1] && GetCycle(channel)!=GetCycle(channel+2) )
	{
		//Shade is used in more than 1 cycles, and the ways it is used are different
		//in cycles, so we can not merge shade with const factors
		return;	
	}

	if( usedIn[0] )	{ cycleVal = GetCycle(channel);cycleNum=0;}
	else			{cycleVal = GetCycle(channel+2);cycleNum=1;}


	//Update to here, Shade is either used only in 1 cycle, or the way it is used are totally
	//the same in different cycles

	if( cycleVal == 0x06000000 || isUsedInCycle(MUX_COMBINED,channel+cycleNum*2) )	// (0-0)*0+Shade
	{
		return;
	}

	//Now we can merge shade with consts
	for( int i=0; i<2; i++ )
	{
		if( usedIn[i] )
		{
			N64CombinerType &m = m_n64Combiners[channel+i*2];
			if( isUsedInCycle(MUX_TEXEL0,i*2+channel) || isUsedInCycle(MUX_TEXEL1,i*2+channel) )
			{
				if( (m.a&MUX_MASK) == MUX_TEXEL0 || (m.a&MUX_MASK) == MUX_TEXEL1 )
				{
					// m.a is texel, can not merge constant with shade
					return;
				}
				else if( (m.b&MUX_MASK) == MUX_TEXEL0 || (m.b&MUX_MASK) == MUX_TEXEL1 )
				{
					// m.b is texel, can not merge constant with shade
					return;
				}
				else if(( (m.c&MUX_MASK) == MUX_TEXEL0 || (m.c&MUX_MASK) == MUX_TEXEL1 ) )
				{
					if( (m.d&MUX_MASK) != MUX_SHADE )
					{
						cycleVal &= 0x0000FFFF;		// A-B
					}
					else if( (m.a&MUX_MASK) == MUX_SHADE || (m.b&MUX_MASK) == MUX_SHADE )
					{
						return;
					}
				}
				else if( (m.d&MUX_MASK) == MUX_TEXEL0 || (m.d&MUX_MASK) == MUX_TEXEL1 )
				{
					cycleVal &= 0x00FFFFFF;		// (A-B)*C
				}
			}
			else
			{
				m.a = m.b = m.c = MUX_0;
				m.d = MUX_SHADE;
				splitType[i*2+channel] = CM_FMT_TYPE_D;
			}
		}
	}

	if( channel == COLOR_CHANNEL )
		m_dwShadeColorChannelFlag = cycleVal;
	else
		m_dwShadeAlphaChannelFlag = cycleVal;
}


void DecodedMux::MergeConstants(void)
{
	// This function should be called afte the mux has been simplified
	// The goal of this function is to merge remain constants and to reduce the
	// total number of constants, so we can implement the mux easiler

	// This function should be called after the MergeShadeWithConstants() function
}


void DecodedMux::UseShadeForConstant(void)
{
	// If shade is not used in the mux, we can use it for constants
	// This function should be called after constants have been merged

	bool doAlphaChannel = true;
	uint8 mask = ~MUX_COMPLEMENT;

	int constants = 0;
	if( isUsed(MUX_ENV) ) constants++;
	if( isUsed(MUX_PRIM) ) constants++;
	if( isUsed(MUX_LODFRAC) ) constants++;
	if( isUsed(MUX_PRIMLODFRAC) ) constants++;

	bool forceToUsed = constants>m_maxConstants;

	if( !isUsedInColorChannel(MUX_SHADE) && (forceToUsed || max(splitType[0], splitType[2]) >= CM_FMT_TYPE_A_MOD_C_ADD_D) )
	{
		int countEnv = Count(MUX_ENV, N64Cycle0RGB, mask) + Count(MUX_ENV, N64Cycle1RGB, mask);
		int countPrim = Count(MUX_PRIM, N64Cycle0RGB, mask) + Count(MUX_PRIM, N64Cycle1RGB, mask);
		if( countEnv+countPrim > 0 )
		{
			if( countPrim >= countEnv )
			{
				//TRACE0("Use Shade for PRIM in color channel");
				ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0RGB);
				ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1RGB);
				m_dwShadeColorChannelFlag = MUX_PRIM;
			}
			else if( countEnv>0 )
			{
				//TRACE0("Use Shade for ENV in color channel");
				ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0RGB);
				ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1RGB);
				m_dwShadeColorChannelFlag = MUX_ENV;
			}

			if( isUsedInColorChannel(MUX_SHADE|MUX_ALPHAREPLICATE, mask) )
			{
				m_dwShadeAlphaChannelFlag = m_dwShadeColorChannelFlag;
				ReplaceVal((uint8)m_dwShadeColorChannelFlag, MUX_SHADE, N64Cycle0Alpha);
				ReplaceVal((uint8)m_dwShadeColorChannelFlag, MUX_SHADE, N64Cycle1Alpha);
				doAlphaChannel = false;
			}
		}
	}

	if( doAlphaChannel && !isUsedInAlphaChannel(MUX_SHADE) && !isUsedInColorChannel(MUX_SHADE|MUX_ALPHAREPLICATE,MUX_MASK_WITH_ALPHA))
	{
		int countEnv = Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
		int countPrim = Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);

		if( forceToUsed || max(splitType[1], splitType[3]) >= CM_FMT_TYPE_A_MOD_C_ADD_D ||
			(max(splitType[0], splitType[2]) >= CM_FMT_TYPE_A_MOD_C_ADD_D && countEnv+countPrim > 0 ) )
		{
			countEnv = Count(MUX_ENV, N64Cycle0Alpha) + Count(MUX_ENV, N64Cycle1Alpha) +
							Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_ENV|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
			countPrim = Count(MUX_PRIM, N64Cycle0Alpha) + Count(MUX_PRIM, N64Cycle1Alpha) +
							Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask) + Count(MUX_PRIM|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
			if( countEnv+countPrim > 0 )
			{
				if( countPrim>0 && m_dwShadeColorChannelFlag == MUX_PRIM )
				{
					//TRACE0("Use Shade for PRIM in alpha channel");
					ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0Alpha);
					ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1Alpha);
					ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
					ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
					m_dwShadeAlphaChannelFlag = MUX_PRIM;
				}				
				else if( countEnv>0 && m_dwShadeColorChannelFlag == MUX_ENV )
				{
					//TRACE0("Use Shade for PRIM in alpha channel");
					ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0Alpha);
					ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1Alpha);
					ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
					ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
					m_dwShadeAlphaChannelFlag = MUX_ENV;
				}				
				else if( countPrim >= countEnv )
				{
					//TRACE0("Use Shade for PRIM in alpha channel");
					ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle0Alpha);
					ReplaceVal(MUX_PRIM, MUX_SHADE, N64Cycle1Alpha);
					ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
					ReplaceVal(MUX_PRIM|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
					m_dwShadeAlphaChannelFlag = MUX_PRIM;
				}
				else if( countEnv>0 )
				{
					//TRACE0("Use Shade for ENV in alpha channel");
					ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle0Alpha);
					ReplaceVal(MUX_ENV, MUX_SHADE, N64Cycle1Alpha);
					ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle0RGB, mask);
					ReplaceVal(MUX_ENV|MUX_ALPHAREPLICATE, MUX_SHADE|MUX_ALPHAREPLICATE, N64Cycle1RGB, mask);
					m_dwShadeAlphaChannelFlag = MUX_ENV;
				}
			}
		}
	}
}

void DecodedMux::UseTextureForConstant(void)
{
	int numofconst = HowManyConstFactors();
	int numOftex = HowManyTextures();

	if( numofconst > m_maxConstants && numOftex < m_maxTextures )
	{
		// We can use a texture for a constant
		for( int i=0; i<2 && numofconst > m_maxConstants ; i++ )
		{
			if( isUsed(MUX_TEXEL0+i) )
			{
				continue;	// can not use this texture
			}

			if( isUsed(MUX_PRIM) )
			{
				ReplaceVal(MUX_PRIM, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_PRIM;
				numofconst--;
				continue;
			}

			if( isUsed(MUX_ENV) )
			{
				ReplaceVal(MUX_ENV, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_ENV;
				numofconst--;
				continue;
			}

			if( isUsed(MUX_LODFRAC) )
			{
				ReplaceVal(MUX_LODFRAC, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_LODFRAC;
				numofconst--;
				continue;
			}

			if( isUsed(MUX_PRIMLODFRAC) )
			{
				ReplaceVal(MUX_PRIMLODFRAC, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_PRIMLODFRAC;
				numofconst--;
				continue;
			}
		}
	}
}


void DecodedMuxForOGL14V2::UseTextureForConstant(void)
{
	bool envused = isUsed(MUX_ENV);
	bool lodused = isUsed(MUX_LODFRAC);
	
	int numofconst = 0;
	if( envused ) numofconst++;
	if( lodused ) numofconst++;

	int numOftex = HowManyTextures();

	if( numofconst > 0 && numOftex < 2 )
	{
		// We can use a texture for a constant
		for( int i=0; i<2 && numofconst > 0 ; i++ )
		{
			if( isUsed(MUX_TEXEL0+i) )
			{
				continue;	// can not use this texture
			}

			if( envused )
			{
				ReplaceVal(MUX_ENV, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_ENV;
				numofconst--;
				envused = false;
				continue;
			}

			if( isUsed(MUX_LODFRAC) )
			{
				ReplaceVal(MUX_LODFRAC, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_LODFRAC;
				numofconst--;
				continue;
			}

			if( isUsed(MUX_PRIMLODFRAC) )
			{
				ReplaceVal(MUX_PRIMLODFRAC, MUX_TEXEL0+i);
				m_ColorTextureFlag[i] = MUX_PRIMLODFRAC;
				numofconst--;
				continue;
			}
		}
	}
}

#ifdef _DEBUG
extern const char *translatedCombTypes[];
void DecodedMux::DisplayMuxString(const char *prompt)
{
	DebuggerAppendMsg("//Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
	Display(false);
	TRACE0("\n");
}

void DecodedMux::DisplaySimpliedMuxString(const char *prompt)
{
	DebuggerAppendMsg("//Simplied Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
	DebuggerAppendMsg("Simplied DWORDs=%08X, %08X, %08X, %08X", m_dWords[0],m_dWords[1],m_dWords[2],m_dWords[3]);
	Display(true);
	DebuggerAppendMsg("Simplfied type: %s", muxTypeStrs[mType]);
	if( m_dwShadeColorChannelFlag != 0 )
	{
		if( m_dwShadeColorChannelFlag == MUX_ENV )
			TRACE0("Shade = ENV in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_PRIM )
			TRACE0("Shade = PRIM in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_LODFRAC )
			TRACE0("Shade = MUX_LODFRAC in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_PRIMLODFRAC )
			TRACE0("Shade = MUX_PRIMLODFRAC in color channel")
		else
			DisplayConstantsWithShade(m_dwShadeColorChannelFlag,COLOR_CHANNEL);
	}
	if( m_dwShadeAlphaChannelFlag != 0 )
	{
		if( m_dwShadeAlphaChannelFlag == MUX_ENV )
			TRACE0("Shade = ENV in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_PRIM )
			TRACE0("Shade = PRIM in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_LODFRAC )
			TRACE0("Shade = MUX_LODFRAC in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_PRIMLODFRAC )
			TRACE0("Shade = MUX_PRIMLODFRAC in alpha channel")
		else
			DisplayConstantsWithShade(m_dwShadeAlphaChannelFlag,ALPHA_CHANNEL);
	}

	for( int i=0; i<2; i++ )
	{
		if( m_ColorTextureFlag[i] != 0 )
		{
			if( m_ColorTextureFlag[i] == MUX_ENV )
				TRACE1("Tex %d = ENV", i)
			else if( m_ColorTextureFlag[i] == MUX_PRIM )
				TRACE1("Tex %d = PRIM", i)
			else if( m_ColorTextureFlag[i] == MUX_LODFRAC )
				TRACE1("Tex %d = MUX_LODFRAC", i)
			else if( m_ColorTextureFlag[i] == MUX_PRIMLODFRAC )
				TRACE1("Tex %d = MUX_PRIMLODFRAC", i)
		}
	}


	TRACE0("\n");
}

void DecodedMux::DisplayConstantsWithShade(uint32 flag,CombineChannel channel)
{
	DebuggerAppendMsg("Shade = %08X in %s channel",flag,channel==COLOR_CHANNEL?"color":"alpha");
}
#else

extern const char *translatedCombTypes[];
void DecodedMux::LogMuxString(const char *prompt, FILE *fp)
{
	fprintf(fp,"//Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
	Display(false,fp);
	TRACE0("\n");
}

void DecodedMux::LogSimpliedMuxString(const char *prompt, FILE *fp)
{
	fprintf(fp,"//Simplied Mux=0x%08x%08x\t%s in %s\n", m_dwMux0, m_dwMux1, prompt, g_curRomInfo.szGameName);
	fprintf(fp,"Simplied DWORDs=%08X, %08X, %08X, %08X\n", m_dWords[0],m_dWords[1],m_dWords[2],m_dWords[3]);
	Display(true,fp);
	fprintf(fp,"Simplfied type: %s", muxTypeStrs[mType]);
	if( m_dwShadeColorChannelFlag != 0 )
	{
		if( m_dwShadeColorChannelFlag == MUX_ENV )
			TRACE0("Shade = ENV in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_PRIM )
		TRACE0("Shade = PRIM in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_LODFRAC )
		TRACE0("Shade = MUX_LODFRAC in color channel")
		else if( m_dwShadeColorChannelFlag == MUX_PRIMLODFRAC )
		TRACE0("Shade = MUX_PRIMLODFRAC in color channel")
		else
		LogConstantsWithShade(m_dwShadeColorChannelFlag,COLOR_CHANNEL,fp);
	}
	if( m_dwShadeAlphaChannelFlag != 0 )
	{
		if( m_dwShadeAlphaChannelFlag == MUX_ENV )
			TRACE0("Shade = ENV in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_PRIM )
		TRACE0("Shade = PRIM in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_LODFRAC )
		TRACE0("Shade = MUX_LODFRAC in alpha channel")
		else if( m_dwShadeAlphaChannelFlag == MUX_PRIMLODFRAC )
		TRACE0("Shade = MUX_PRIMLODFRAC in alpha channel")
		else
		LogConstantsWithShade(m_dwShadeAlphaChannelFlag,ALPHA_CHANNEL,fp);
	}

	for( int i=0; i<2; i++ )
	{
		if( m_ColorTextureFlag[i] != 0 )
		{
			if( m_ColorTextureFlag[i] == MUX_ENV )
				TRACE1("Tex %d = ENV", i)
			else if( m_ColorTextureFlag[i] == MUX_PRIM )
			TRACE1("Tex %d = PRIM", i)
			else if( m_ColorTextureFlag[i] == MUX_LODFRAC )
			TRACE1("Tex %d = MUX_LODFRAC", i)
			else if( m_ColorTextureFlag[i] == MUX_PRIMLODFRAC )
			TRACE1("Tex %d = MUX_PRIMLODFRAC", i)
		}
	}


	TRACE0("\n");
}

void DecodedMux::LogConstantsWithShade(uint32 flag,CombineChannel channel, FILE *fp)
{
	fprintf(fp,"Shade = %08X in %s channel",flag,channel==COLOR_CHANNEL?"color":"alpha");
}
#endif


void DecodedMux::To_AB_Add_CD_Format(void)	// Use by TNT,Geforce
{
	// This function should be called after calling reformat 
	// This function will not be called by default, can be called optionally
	// by TNT/Geforce combiner compilers

	for( int i=0; i<2; i++ )
	{
		N64CombinerType &m0 = m_n64Combiners[i];
		N64CombinerType &m1 = m_n64Combiners[i+2];
		switch( splitType[i] )
		{
		case CM_FMT_TYPE_A_SUB_B_ADD_D:		// = A-B+D		can not map very well to D3D in 1 stage
			if( splitType[i+2] == CM_FMT_TYPE_NOT_USED )
			{
				m1.a = m0.d;
				m1.d = MUX_COMBINED;
				splitType[i+2] = CM_FMT_TYPE_A_ADD_D;

				m0.d = MUX_0;
				splitType[i] = CM_FMT_TYPE_A_SUB_B;
			}
			else if( splitType[i+2] == CM_FMT_TYPE_A_MOD_C )
			{
				if( (m1.c&MUX_MASK) == MUX_COMBINED )	swap(m1.a, m1.c);
				m1.b = m1.d = m1.c;
				m1.c = (m0.d | (m1.a & (~MUX_MASK)));
				splitType[i+2] = CM_FMT_TYPE_AB_ADD_CD;

				m0.d = MUX_0;
				splitType[i] = CM_FMT_TYPE_A_SUB_B;
			}
			break;
		case CM_FMT_TYPE_A_SUB_B_MOD_C:		// = (A-B)*C	can not map very well to D3D in 1 stage
			m0.d = m0.b;
			m0.b = m0.c;
			splitType[i] = CM_FMT_TYPE_AB_SUB_CD;
			break;
		case CM_FMT_TYPE_A_ADD_B_MOD_C:		// = (A+B)*C	can not map very well to D3D in 1 stage
			m0.d = m0.b;
			m0.b = m0.c;
			splitType[i] = CM_FMT_TYPE_AB_ADD_CD;
			break;
		case CM_FMT_TYPE_A_B_C_D:			// = (A-B)*C+D
		case CM_FMT_TYPE_A_B_C_A:			// = (A-B)*C+D
			if( splitType[i+2] == CM_FMT_TYPE_NOT_USED )
			{
				m1.a = m0.d;
				m1.d = MUX_COMBINED;
				splitType[i+2] = CM_FMT_TYPE_A_ADD_D;

				m0.d = m0.b;
				m0.b = m0.c;
				splitType[i] = CM_FMT_TYPE_AB_SUB_CD;
			}
			else if( splitType[i+2] == CM_FMT_TYPE_A_MOD_C )
			{
				if( (m1.c&MUX_MASK) == MUX_COMBINED )	swap(m1.a, m1.c);
				m1.b = m1.d = m1.c;
				m1.c = (m0.d | (m1.a & (~MUX_MASK)));
				splitType[i+2] = CM_FMT_TYPE_AB_ADD_CD;

				m0.d = m0.b;
				m0.b = m0.c;
				splitType[i] = CM_FMT_TYPE_AB_ADD_CD;
			}
			break;
		}
	}
}

void DecodedMux::To_AB_Add_C_Format(void)	// Use by ATI Radeon
{
	// This function should be called after calling reformat
	// This function will not be called by default, can be called optionally
	// by ATI combiner compilers
}

void DecodedMux::CheckCombineInCycle1(void)
{
	if( isUsedInCycle(MUX_COMBINED,0,COLOR_CHANNEL) )
	{
		ReplaceVal(MUX_COMBINED, MUX_SHADE, 0);
	}

	if( isUsedInCycle(MUX_COMBALPHA,0,COLOR_CHANNEL) )
	{
		ReplaceVal(MUX_COMBALPHA, MUX_SHADE|MUX_ALPHAREPLICATE, 0);
	}

	if( isUsedInCycle(MUX_COMBINED,0,ALPHA_CHANNEL) )
	{
		if( cA0 == MUX_COMBINED && cRGB0 == MUX_LODFRAC && bRGB0 == dRGB0 && bA0 == dA0 )
		{
			cA0 = MUX_LODFRAC;
		}
		else
		{
			ReplaceVal(MUX_COMBINED, MUX_SHADE, 1);
		}
	}
	if( isUsedInCycle(MUX_COMBALPHA,0,ALPHA_CHANNEL) )
	{
		ReplaceVal(MUX_COMBALPHA, MUX_SHADE, 1);
	}
}

void DecodedMux::SplitComplexStages()
{
	for( int i=0; i<2; i++)	// Color channel and alpha channel
	{
		if( splitType[i+2] != CM_FMT_TYPE_NOT_USED )	
			continue;

		N64CombinerType &m = m_n64Combiners[i];
		N64CombinerType &m2 = m_n64Combiners[i+2];
		
		switch( splitType[i] )
		{
		case CM_FMT_TYPE_A_MOD_C_ADD_D:		// = A*C+D		can mapped to D3D MULTIPLYADD(arg1,arg2,arg0)
			m2.a = m.d;
			m2.d = MUX_COMBINED;
			m2.c = MUX_1;
			m2.b = 0;
			splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
			m.d = MUX_0;
			splitType[i] = CM_FMT_TYPE_A_MOD_C;
			break;
		case CM_FMT_TYPE_A_SUB_B_ADD_D:		// = A-B+D		can not map very well to D3D in 1 stage
			m2.a = m.d;
			m2.d = MUX_COMBINED;
			m2.c = MUX_1;
			m2.b=0;
			splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
			m.d = MUX_0;
			splitType[i] = CM_FMT_TYPE_A_SUB_B;
			break;
		case CM_FMT_TYPE_A_SUB_B_MOD_C:		// = (A-B)*C	can not map very well to D3D in 1 stage
			m2.a = m.c;
			m2.c = MUX_COMBINED;
			m2.d = m2.b=0;
			splitType[i+2] = CM_FMT_TYPE_A_MOD_C;
			m.c = MUX_1;
			splitType[i] = CM_FMT_TYPE_A_SUB_B;
			break;
		case CM_FMT_TYPE_A_ADD_B_MOD_C:		// = (A+B)*C	can not map very well to D3D in 1 stage
			m2.a = m.c;
			m2.c = MUX_COMBINED;
			m2.d = m2.b = 0;
			splitType[i+2] = CM_FMT_TYPE_A_MOD_C;
			m.c = MUX_1;
			m.d = m.b;
			m.b = MUX_0;
			splitType[i] = CM_FMT_TYPE_A_ADD_D;
			break;
		case CM_FMT_TYPE_A_B_C_D:			// = (A-B)*C+D	can not map very well to D3D in 1 stage
			m2.a = m.d;
			m2.d = MUX_COMBINED;
			m2.c = MUX_1;
			m2.b = 0;
			splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
			m.d = MUX_0;
			splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
			break;
		case CM_FMT_TYPE_A_B_C_A:			// = (A-B)*C+A	can not map very well to D3D in 1 stage
			m2.a = m.d;
			m2.d = MUX_COMBINED;
			m2.c = MUX_1;
			m2.b = 0;
			splitType[i+2] = CM_FMT_TYPE_A_ADD_D;
			m.d = MUX_0;
			splitType[i] = CM_FMT_TYPE_A_SUB_B_MOD_C;
			break;
		}
	}
	//Reformat();
	//UseShadeForConstant();
}


void DecodedMux::ConvertLODFracTo0()
{
	ReplaceVal(MUX_LODFRAC,MUX_0);
	ReplaceVal(MUX_PRIMLODFRAC,MUX_0);
}


void DecodedMux::Hack(void)
{
	if( options.enableHackForGames == HACK_FOR_TONYHAWK )
	{
		if( gRSP.curTile == 1 )
		{
			ReplaceVal(MUX_TEXEL1, MUX_TEXEL0);
		}
	}
	else if( options.enableHackForGames == HACK_FOR_ZELDA || options.enableHackForGames == HACK_FOR_ZELDA_MM)
	{
		if( m_dwMux1 == 0xfffd9238 && m_dwMux0 == 0x00ffadff )
		{
			ReplaceVal(MUX_TEXEL1, MUX_TEXEL0);
		}
		else if( m_dwMux1 == 0xff5bfff8 && m_dwMux0 == 0x00121603 )
		{
			// The Zelda road trace
			ReplaceVal(MUX_TEXEL1, MUX_0);
		}
	}
	else if( options.enableHackForGames == HACK_FOR_MARIO_TENNIS )
	{
		if( m_dwMux1 == 0xffebdbc0 && m_dwMux0 == 0x00ffb9ff )
		{
			// Player shadow
			//m_decodedMux.dRGB0 = MUX_TEXEL0;
			//m_decodedMux.dRGB1 = MUX_COMBINED;
			cA1 = MUX_TEXEL0;
		}
	}
	else if( options.enableHackForGames == HACK_FOR_MARIO_GOLF )
	{
		// Hack for Mario Golf
		if( m_dwMux1 == 0xf1ffca7e || m_dwMux0 == 0x00115407 )
		{
			// The grass
			ReplaceVal(MUX_TEXEL0, MUX_TEXEL1);
		}
	}
	else if( options.enableHackForGames == HACK_FOR_TOPGEARRALLY )
	{
		//Mux=0x00317e025ffef3fa	Used in TOP GEAR RALLY
		//Color0: (PRIM - ENV) * TEXEL1 + ENV
		//Color1: (COMBINED - 0) * TEXEL1 + 0
		//Alpha0: (0 - 0) * 0 + TEXEL0
		//Alpha1: (0 - 0) * 0 + TEXEL1
		if( m_dwMux1 == 0x5ffef3fa || m_dwMux0 == 0x00317e02 )
		{
			// The grass
			//ReplaceVal(MUX_TEXEL0, MUX_TEXEL1);
			dA1 = MUX_COMBINED;
			//aA1 = MUX_COMBINED;
			//cA1 = MUX_TEXEL1;
			//dA1 = MUX_0;
			cRGB1 = MUX_TEXEL0;
		}
	}



}

