/*
Copyright (C) 2003 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 <algorithm>

#include "GeneralCombiner.h"
#include "Combiner.h"
#include "Debugger.h"

extern const int numOf3StageCombiners;
extern const int numOf2StageCombiners;
extern GeneralCombinerInfo CombinerTable2Stages[];
extern GeneralCombinerInfo CombinerTable3Stages[];

CGeneralCombiner::CGeneralCombiner()
{
    m_lastGeneralIndex=0;
    m_ppGeneralDecodedMux=NULL;

    m_bTxtOpAdd=true;
    m_bTxtOpSub=false;
    m_bTxtOpLerp=false;         
    m_bTxtOpAddSmooth=false;        
    m_bTxtOpBlendCurAlpha=false;    
    m_bTxtOpBlendDifAlpha=true; 
    m_bTxtOpBlendFacAlpha=false;    
    m_bTxtOpBlendTxtAlpha=true; 
    m_bTxtOpMulAdd=false;       

    m_dwGeneralMaxStages=2;
}

bool isTex(uint32 val)
{
    return ( (val&MUX_MASK) == MUX_TEXEL0 || (val&MUX_MASK) == MUX_TEXEL1 );
}
int toTex(uint32 val)
{
    return (val&MUX_MASK)-MUX_TEXEL0;
}

bool isComb(uint32 val)
{
    return (val&MUX_MASK)==MUX_COMBINED;
}

#ifdef DEBUGGER
const char* BlendFuncStr[] = {
    "Enable both",
    "Disable alpha",
    "Disable color",
    "Disable both",
    "Color one",
    "Alpha one",
};
const char* cmopstrs[] = {
    "Sel",
    "Mod",
    "Add",
    "Sub",
    "Lerp",
    "AddSmooth",            
    "BlCurA",   
    "BlDifA",   
    "BlFacA",   
    "BlTexA",   
    "MulAdd",           
};

void CGeneralCombiner::General_DisplayBlendingStageInfo(GeneralCombinerInfo &gci)
{
    char str1[30],str2[30],str3[30];
    DebuggerAppendMsg("\nStages:%d, Alpha:%s, Factor:%s, Specular:%s Dif Color:0x%X Dif Alpha:0x%X\n", 
        gci.nStages, BlendFuncStr[gci.blendingFunc], DecodedMux::FormatStr((uint8)gci.TFactor,str1),
        DecodedMux::FormatStr((uint8)gci.specularPostOp,str2), gci.m_dwShadeColorChannelFlag, gci.m_dwShadeAlphaChannelFlag);

    for( int i=0; i<gci.nStages; i++ )
    {
        GeneralCombineStage &s = gci.stages[i];
        DebuggerAppendMsg("%d:Color: %s - %s, %s, %s%s\n", i,
            cmopstrs[s.colorOp.op], DecodedMux::FormatStr((uint8)s.colorOp.Arg1, str1),     s.colorOp.Arg2==CM_IGNORE?"":DecodedMux::FormatStr((uint8)s.colorOp.Arg2, str2), 
            s.colorOp.Arg0==CM_IGNORE?"":DecodedMux::FormatStr((uint8)s.colorOp.Arg0, str3),
            s.dwTexture!=0?" -Tex1":"");
    }
    
    for( int i=0; i<gci.nStages; i++ )
    {
        GeneralCombineStage &s = gci.stages[i];
        DebuggerAppendMsg("%d:Alpha: %s - %s, %s, %s%s\n", i,
            cmopstrs[s.alphaOp.op], DecodedMux::FormatStr((uint8)s.alphaOp.Arg1, str1), 
            s.alphaOp.Arg2==CM_IGNORE?"":DecodedMux::FormatStr((uint8)s.alphaOp.Arg2, str2),
            s.alphaOp.Arg0==CM_IGNORE?"":DecodedMux::FormatStr((uint8)s.alphaOp.Arg0, str3),
            s.dwTexture!=0?" -Tex1":"");
    }
    TRACE0("\n\n");
}
#endif


///////////////////////////////////////
// Combiner gci generating functions //
///////////////////////////////////////

bool textureUsedInStage[8][2];
bool resultIsGood;

void CGeneralCombiner::GenCI_Init(GeneralCombinerInfo &gci)
{
    gci.specularPostOp=gci.TFactor=MUX_0;

    gci.blendingFunc = ENABLE_BOTH;
    resultIsGood = true;

    //After the mux is reformated and simplified, we can use it to generate combine stages
    //return false if we can not generate it

    for( int i=0; i<8; i++)
    {
        gci.stages[i].dwTexture = 0;
        textureUsedInStage[i][0] = false;       // For color
        textureUsedInStage[i][1] = false;       // For alpha
        gci.stages[i].bTextureUsed = false;
        gci.stages[i].dwTexture = 0;
        gci.stages[i].colorOp.op = gci.stages[i].alphaOp.op = CM_REPLACE;
        gci.stages[i].colorOp.Arg1 = gci.stages[i].alphaOp.Arg1 = MUX_COMBINED;
        gci.stages[i].colorOp.Arg2 = gci.stages[i].alphaOp.Arg2 = CM_IGNORE;
        gci.stages[i].colorOp.Arg0 = gci.stages[i].alphaOp.Arg0 = CM_IGNORE;
    }

    DecodedMux &mux = *(*m_ppGeneralDecodedMux);

    // Check some special cases of alpha channel
    if( mux.splitType[N64Cycle0Alpha]==CM_FMT_TYPE_D && mux.splitType[N64Cycle1Alpha]==CM_FMT_TYPE_NOT_USED )
    {
        //if( mux.m_n64Combiners[N64Cycle0Alpha].d == MUX_0 )
        //  gci.blendingFunc = DISABLE_COLOR;
        //else 
        if( mux.m_n64Combiners[N64Cycle0Alpha].d == MUX_1  )
            gci.blendingFunc = DISABLE_ALPHA;
    }
    else if( mux.splitType[N64Cycle1Alpha]==CM_FMT_TYPE_D )
    {
        //if( mux.m_n64Combiners[N64Cycle1Alpha].d == MUX_0 )
        //  gci.blendingFunc = DISABLE_COLOR;
        //else 
        if( mux.m_n64Combiners[N64Cycle1Alpha].d == MUX_1  )
            gci.blendingFunc = DISABLE_ALPHA;
    }

    if( mux.splitType[N64Cycle0RGB]==CM_FMT_TYPE_D && mux.splitType[N64Cycle1RGB]==CM_FMT_TYPE_NOT_USED )
    {
        if( mux.m_n64Combiners[N64Cycle0RGB].d == MUX_0 )
            gci.blendingFunc = DISABLE_COLOR;
    }

}

int CGeneralCombiner::GenCI_Type_D(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
    if( ( m.d == MUX_0 || m.d == MUX_1 ) && curN64Stage==1 )
    {
        //if( m.d == MUX_0 )
        //  gci.blendingFunc = DISABLE_COLOR;
        //if( m.d == MUX_1 )
        //  gci.blendingFunc = DISABLE_ALPHA;

        op->op = CM_REPLACE;
        op->Arg1 = MUX_COMBINED;
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
    }
    else
    {
        if( isTex(m.d) )    Check1TxtrForAlpha(curN64Stage, curStage, gci, toTex(m.d));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op = CM_REPLACE;
        op->Arg1 = m.d;
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
    }

    if( !gci.stages[curStage].bTextureUsed )
        gci.stages[curStage].dwTexture = GetTexelNumber(m);
    textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m);
    return curStage;
}

int CGeneralCombiner::GenCI_Type_A_MOD_C(int curN64Stage, int curStage, GeneralCombinerInfo &gci, uint32 dxop)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    if( CountTexel1Cycle(m) == 2 )
    {
        // As we can not use both texture in one stage
        // we split them to two stages
        // Stage1: SELECT   txt1
        // Stage2: MOD      txt2

        if( gci.stages[curStage].bTextureUsed && gci.stages[curStage].dwTexture != (unsigned int)toTex(m.a) )
            swap(m.a,m.c);

        op->op =CM_REPLACE;
        op->Arg1 = m.a;
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
        gci.stages[curStage].dwTexture = toTex(m.a);
        textureUsedInStage[curStage][curN64Stage%2] = true;

        NextStage(curStage);
        Check1TxtrForAlpha(curN64Stage, curStage, gci, toTex(m.c));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op  =dxop;
        op->Arg1 = (m.c);
        op->Arg2 = MUX_COMBINED;
        op->Arg0 = CM_IGNORE;
        gci.stages[curStage].dwTexture = toTex(m.c);
        textureUsedInStage[curStage][curN64Stage%2] = true;
    }
    else
    {
        if( CountTexel1Cycle(m) == 1)
        {
            Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
            op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
        }

        op->op = dxop;
        op->Arg1 = (m.a);
        op->Arg2 = (m.c);
        op->Arg0 = CM_IGNORE;
        if( !gci.stages[curStage].bTextureUsed )
            gci.stages[curStage].dwTexture = GetTexelNumber(m);
        textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m);
    }

    return curStage;
}
int CGeneralCombiner::GenCI_Type_A_ADD_D(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    uint32 opToUse = m_bTxtOpAdd?CM_ADD:CM_MODULATE;
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    swap(m.c, m.d);
    curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci, opToUse);
    swap(m.c, m.d);
    return curStage;
}

int CGeneralCombiner::GenCI_Type_A_SUB_B(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    if( !m_bTxtOpSub )
    {
        swap(m.c, m.b);
        curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci);
        swap(m.c, m.b);
        return curStage;
    }

    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    if( CountTexel1Cycle(m) == 2 )
    {
        Check1TxtrForAlpha(curN64Stage, curStage, gci, toTex(m.b));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op =CM_REPLACE;
        op->Arg1 = (m.b);
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
        gci.stages[curStage].dwTexture = toTex(m.b);
        textureUsedInStage[curStage][curN64Stage%2] = true;

        NextStage(curStage);
        Check1TxtrForAlpha(curN64Stage, curStage, gci, toTex(m.a));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op  =CM_SUBTRACT;
        op->Arg1 = (m.a);
        op->Arg2 = MUX_COMBINED;
        op->Arg0 = CM_IGNORE;
        gci.stages[curStage].dwTexture = toTex(m.a);
        textureUsedInStage[curStage][curN64Stage%2] = true;
    }
    else
    {
        if( CountTexel1Cycle(m) == 1)
        {
            Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
            op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
        }

        op->op = CM_SUBTRACT;
        op->Arg1 = (m.a);
        op->Arg2 = (m.b);
        op->Arg0 = CM_IGNORE;
        if( !gci.stages[curStage].bTextureUsed )
            gci.stages[curStage].dwTexture = GetTexelNumber(m);
        textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m);
    }
    return curStage;
}

int CGeneralCombiner::GenCI_Type_A_MOD_C_ADD_D(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    if( !m_bTxtOpMulAdd )
    {
        N64CombinerType save = m;
        m.d = MUX_0;
        curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci);
        m = save;
        m.c = MUX_0;
        m.a = MUX_COMBINED;
        NextStage(curStage);
        curStage = GenCI_Type_A_ADD_D(curN64Stage, curStage, gci);
        m = save;
        return curStage;
    }

    if( CountTexel1Cycle(m) == 2 )
    {
        if( !gci.stages[curStage].bTextureUsed )
        {
            gci.stages[curStage].dwTexture = 0;
            gci.stages[curStage].bTextureUsed = true;
        }

        op->op = CM_REPLACE;
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
        op->Arg1 = MUX_TEXEL0 + gci.stages[curStage].dwTexture ;

        N64CombinerType m2 = m;

        uint8* vals = (uint8*)&m2;
        for( int i=0; i<4; i++ )
        {
            if( (unsigned int)(vals[i]&MUX_MASK) == MUX_TEXEL0 + gci.stages[curStage].dwTexture )
            {
                vals[i] = MUX_COMBINED | (vals[i]&0xe0);
            }
        }

        NextStage(curStage);

        Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m2));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op = CM_MULTIPLYADD;
        op->Arg1 = m2.a;
        op->Arg2 = m2.c;
        op->Arg0 = m2.d;
        if( !gci.stages[curStage].bTextureUsed )
            gci.stages[curStage].dwTexture = GetTexelNumber(m2);
        textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m2);
    }
    else
    {
        Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

        op->op = CM_MULTIPLYADD;
        op->Arg1 = (m.a);
        op->Arg2 = (m.c);
        op->Arg0 = (m.d);
        if( !gci.stages[curStage].bTextureUsed )
            gci.stages[curStage].dwTexture = GetTexelNumber(m);
        textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m);
    }

    return curStage;
}


int CGeneralCombiner::GenCI_Type_A_LERP_B_C(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    N64CombinerType save = m;

    if( CountTexel1Cycle(m) == 2 )
    {
        // There are two textures
        int texToUse = CheckWhichTexToUseInThisStage(curN64Stage, curStage, gci);
        op->op =CM_REPLACE;
        op->Arg1 = (MUX_TEXEL0+texToUse);
        op->Arg2 = CM_IGNORE;
        op->Arg0 = CM_IGNORE;
        gci.stages[curStage].dwTexture = texToUse;
        textureUsedInStage[curStage][curN64Stage%2] = true;

        (*m_ppGeneralDecodedMux)->ReplaceVal(MUX_TEXEL0+texToUse, MUX_COMBINED, curN64Stage);
        NextStage(curStage);
        Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
        op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
    }

    // Now we have only 1 texture left

    Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
    op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    if( m.a == MUX_1 )
    {
        op->op = CM_ADDSMOOTH;
        op->Arg1 = (m.b);
        op->Arg2 = (m.c);
        op->Arg0 = CM_IGNORE;
    }
    else if( m.a == MUX_0 )
    {
        op->op = CM_MODULATE;
        m.a = 0;
        op->Arg1 = (m.b);
        op->Arg2 = (m.c^MUX_COMPLEMENT);
        op->Arg0 = CM_IGNORE;
    }
    else
    {
        if( ((m.c&MUX_ALPHAREPLICATE) || (curN64Stage%2)==1 || m_bTxtOpLerp == false) && ((m.c&MUX_MASK)==MUX_SHADE || (m.c&MUX_MASK)==MUX_COMBINED || (m.c&MUX_MASK)==MUX_TEXEL0 || (m.c&MUX_MASK)==MUX_TEXEL1  ) )
        {
            if( curN64Stage == 2 && (m.c&MUX_ALPHAREPLICATE) == 0 )
            {
                op->op = CM_MODULATE;
                op->Arg1 = m.b;
                op->Arg2 = m.c|MUX_COMPLEMENT;
                op->Arg0 = CM_IGNORE;
                resultIsGood = false;
            }
            else
            {
                if( (m.c&MUX_MASK)==MUX_SHADE )
                {
                    op->op = CM_BLENDDIFFUSEALPHA;
                }
                else if( (m.c&MUX_MASK) == MUX_COMBINED )
                {
                    op->op = CM_BLENDCURRENTALPHA;
                }
                else if( (m.c&MUX_MASK) == MUX_TEXEL0 )
                {
                    op->op = CM_BLENDTEXTUREALPHA;
                }
                else if( (m.c&MUX_MASK)==MUX_TEXEL1 )
                {
                    op->op = CM_BLENDTEXTUREALPHA;
                }
                else
                {
                    op->op = CM_BLENDDIFFUSEALPHA;
                }
                op->Arg1 = (m.a);
                op->Arg2 = (m.b);
                op->Arg0 = m.c|MUX_ALPHAREPLICATE;
            }
        }
        else
        {
            if( ((m.c&MUX_ALPHAREPLICATE) || (curN64Stage%2)==1 || m_bTxtOpLerp == false) && ((((m.c&MUX_MASK)==MUX_ENV) || ((m.c&MUX_MASK)==MUX_PRIM)) ))
            {
                op->op = CM_BLENDFACTORALPHA;
                op->Arg1 = (m.a);
                op->Arg2 = (m.b);
                op->Arg0 = m.c|MUX_ALPHAREPLICATE;
            }
            else
            {
                op->op = CM_INTERPOLATE;
                op->Arg0 = (m.c);
                op->Arg1 = (m.a);
                op->Arg2 = (m.b);
            }
        }
    }
    gci.stages[curStage].dwTexture = GetTexelNumber(m);
    textureUsedInStage[curStage][curN64Stage%2] = IsTxtrUsed(m);

    m = save;
    return curStage;
}


int CGeneralCombiner::GenCI_Type_A_B_C_D(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

    N64CombinerType save = m;
    if( CountTexel1Cycle(m) == 2 )
    {
        if( isTex(m.a) && !isTex(m.c) && curN64Stage == 0 && isTex(m.d) && toTex(m.a) != toTex(m.d) )
        {
            if( m_dwGeneralMaxStages >= 4 )
            {
                op->op = CM_SUBTRACT;
                op->Arg1 = m.a;
                op->Arg2 = m.b;
                op->Arg0 = CM_IGNORE;
                gci.stages[curStage].dwTexture = toTex(m.a);
                textureUsedInStage[curStage][curN64Stage%2] = true;
                NextStage(curStage);
                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
                op->op = CM_MULTIPLYADD;
                op->Arg1 = MUX_COMBINED;
                op->Arg2 = m.c;
                op->Arg0 = m.d;
                gci.stages[curStage].dwTexture = toTex(m.d);
                textureUsedInStage[curStage][curN64Stage%2] = true;
                resultIsGood = true;
            }
            else
            {
                op->op = CM_MODULATE;
                op->Arg1 = m.a;
                op->Arg2 = m.c;
                op->Arg0 = CM_IGNORE;
                gci.stages[curStage].dwTexture = toTex(m.a);
                textureUsedInStage[curStage][curN64Stage%2] = true;
                NextStage(curStage);
                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);
                op->op = CM_ADD;
                op->Arg1 = MUX_COMBINED;
                op->Arg2 = m.d;
                op->Arg0 = CM_IGNORE;
                gci.stages[curStage].dwTexture = toTex(m.d);
                textureUsedInStage[curStage][curN64Stage%2] = true;
                resultIsGood = false;
            }
        }
        else
        {
            // There are two textures
            int texToUse = CheckWhichTexToUseInThisStage(curN64Stage, curStage, gci);
            op->op =CM_REPLACE;
            op->Arg1 = (MUX_TEXEL0+texToUse);
            op->Arg2 = CM_IGNORE;
            op->Arg0 = CM_IGNORE;
            gci.stages[curStage].dwTexture = texToUse;
            textureUsedInStage[curStage][curN64Stage%2] = true;

            (*m_ppGeneralDecodedMux)->ReplaceVal(MUX_TEXEL0+texToUse, MUX_COMBINED, curN64Stage);

            NextStage(curStage);
            Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
            op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + (curN64Stage%2);

            m.a = MUX_COMBINED;
            m.c = MUX_TEXEL0+(1-texToUse);
            m.b = m.d = 0;
            curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci);
        }
    }
    else if( CountTexel1Cycle(m) == 1 )
    {
        if( m_dwGeneralMaxStages < 4 )
        {
            Check1TxtrForAlpha(curN64Stage, curStage, gci, GetTexelNumber(m));
            op->Arg1 = (MUX_TEXEL0+GetTexelNumber(m));
            if( (*m_ppGeneralDecodedMux)->isUsedInCycle(MUX_SHADE, curN64Stage) )
            {
                op->op =CM_MODULATE;
                op->Arg2 = MUX_SHADE;
            }
            else
            {
                op->op =CM_REPLACE;
                op->Arg2 = 0;
            }
            op->Arg0 = CM_IGNORE;
            gci.stages[curStage].dwTexture = GetTexelNumber(m);
            textureUsedInStage[curStage][curN64Stage%2] = true;
        }
        else
        {
            curStage = GenCI_Type_A_SUB_B_MOD_C(curN64Stage, curStage, gci);
            m.a = MUX_COMBINED;
            NextStage(curStage);
            curStage = GenCI_Type_A_ADD_D(curN64Stage, curStage, gci);
        }
    }
    else
    {
        m.d = 0;
        curStage = GenCI_Type_A_SUB_B_MOD_C(curN64Stage, curStage, gci);
        m = save;
        m.a = MUX_COMBINED;
        m.b = m.c = 0;
        NextStage(curStage);
        curStage = GenCI_Type_A_ADD_D(curN64Stage, curStage, gci);
    }

    m = save;
    return curStage;
}

int CGeneralCombiner::GenCI_Type_A_SUB_B_ADD_D(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];

    N64CombinerType save = m;
    m.d = MUX_0;
    curStage = GenCI_Type_A_SUB_B(curN64Stage, curStage, gci);
    m = save;
    m.a = MUX_COMBINED;
    m.b = MUX_0;
    NextStage(curStage);
    curStage = GenCI_Type_A_ADD_D(curN64Stage, curStage, gci);
    m = save;

    return curStage;
}


int CGeneralCombiner::GenCI_Type_A_ADD_B_MOD_C(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];

    N64CombinerType save = m;
    m.d = m.b; m.b = 0;
    curStage = GenCI_Type_A_ADD_D(curN64Stage, curStage, gci);
    m = save;
    m.b = MUX_0;
    m.a = MUX_COMBINED;
    NextStage(curStage);
    curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci);
    m = save;

    return curStage;
}

int CGeneralCombiner::GenCI_Type_A_B_C_A(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    // We can not do too much with this type, it is not a bad idea to use LERP to simplify it.
    //return GenCI_Type_A_LERP_B_C(curN64Stage, curStage, gci);
    return GenCI_Type_A_B_C_D(curN64Stage, curStage, gci);
}

int CGeneralCombiner::GenCI_Type_A_SUB_B_MOD_C(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];

    N64CombinerType save = m;
    m.c = MUX_0;
    curStage = GenCI_Type_A_SUB_B(curN64Stage, curStage, gci);
    m = save;
    m.b = MUX_0;
    m.a = MUX_COMBINED;
    NextStage(curStage);
    curStage = GenCI_Type_A_MOD_C(curN64Stage, curStage, gci);
    m = save;

    return curStage;
}

 /////////////////////////////////////
 // End of gci generating functions //
 /////////////////////////////////////


void CGeneralCombiner::SkipStage(StageOperate &op, int &curStage)
{
    op.op = CM_REPLACE;
    op.Arg1 = MUX_COMBINED;
    op.Arg2 = CM_IGNORE;
    op.Arg0 = CM_IGNORE;
    NextStage(curStage);
}

void CGeneralCombiner::NextStage(int &curStage)
{
    if( curStage < m_dwGeneralMaxStages-1 )
    {
        curStage++;
    }
    else
    {
        curStage++;
        resultIsGood = false;
        TRACE0("Stage overflow");
    }
}

void CGeneralCombiner::Check1TxtrForAlpha(int curN64Stage, int &curStage, GeneralCombinerInfo &gci, int tex)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    if( curN64Stage%2 && IsTxtrUsed(m) )
    {
        while (curStage<m_dwGeneralMaxStages-1 && textureUsedInStage[curStage][0] && gci.stages[curStage].dwTexture != (unsigned int)(tex) )
        {
            StageOperate &op = ((StageOperate*)(&(gci.stages[curStage].colorOp)))[curN64Stage%2];
            SkipStage(op, curStage);
        }
    }
}


int CGeneralCombiner::Check2TxtrForAlpha(int curN64Stage, int &curStage, GeneralCombinerInfo &gci, int tex1, int tex2)
{
    N64CombinerType &m = (*m_ppGeneralDecodedMux)->m_n64Combiners[curN64Stage];
    if( curN64Stage%2 && IsTxtrUsed(m) )
    {
        if( tex1 == tex2 )
        {
            while (curStage<m_dwGeneralMaxStages-1 && textureUsedInStage[curStage][0] && gci.stages[curStage].dwTexture != (unsigned int)tex1 )
            {
                StageOperate &op = ((StageOperate*)(&(gci.stages[curStage].colorOp)))[curN64Stage%2];
                SkipStage(op, curStage);
            }
            return 1;
        }
        else
        {
            int stage1 = curStage;
            int stage2 = curStage;

            while (stage1<m_dwGeneralMaxStages-1 && textureUsedInStage[stage1][0] && gci.stages[stage1].dwTexture != (unsigned int)tex1 )
            {
                StageOperate &op = ((StageOperate*)(&(gci.stages[stage1].colorOp)))[curN64Stage%2];
                SkipStage(op, stage1);
            }

            while (stage2<m_dwGeneralMaxStages-1 && textureUsedInStage[stage2][0] && gci.stages[stage2].dwTexture != (unsigned int)tex2 )
            {
                StageOperate &op = ((StageOperate*)(&(gci.stages[stage2].colorOp)))[curN64Stage%2];
                SkipStage(op, stage2);
            }

            if( stage1 <= stage2 )
            {
                curStage = stage1;
                return 1;
            }
            else
            {
                curStage = stage2;
                return 2;
            }
        }
    }
    else
    {
        return 0;
    }
}


int CGeneralCombiner::CheckWhichTexToUseInThisStage(int curN64Stage, int curStage, GeneralCombinerInfo &gci)
{
    // There are two texels to used, which one I should use in the current DirectX stage?
    if( curN64Stage%2 )
    {
        if( !textureUsedInStage[curStage][0] )
            return 0;
        else
            return gci.stages[curStage].dwTexture;
    }
    else
    {
        return 0;
    }
}

/*
 *  
 */

int CGeneralCombiner::ParseDecodedMux()
{
    GeneralCombinerInfo gci;
    int stages[2];

    DecodedMux &mux = *(*m_ppGeneralDecodedMux);

    GenCI_Init(gci);

    for( int i=0; i<2; i++ )
    {
        //i=0       Color Channel
        //i=1       Alpha Channel

        stages[i] = 0;
        int n=0;    //stage count

        for( int j=0; j<2; j++ )
        {
            switch( mux.splitType[i+j*2] )
            {
            case CM_FMT_TYPE_NOT_USED:
                continue;
            case CM_FMT_TYPE_D:     // = D
                // Alpha channel is using different texture from color channel
                // and the color channel has already used texture, so alpha
                // channel can not use different texture for this stage anymore,
                // alpha channel need to skip a stage
                n = GenCI_Type_D(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_ADD_D:       // = A+D
                n=GenCI_Type_A_ADD_D(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_MOD_C:       // = A*C        can mapped to MOD(arg1,arg2)
                n=GenCI_Type_A_MOD_C(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_SUB_B:       // = A-B        can mapped to SUB(arg1,arg2)
                n=GenCI_Type_A_SUB_B(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_MOD_C_ADD_D:     // = A*C+D      can mapped to MULTIPLYADD(arg1,arg2,arg0)
                n=GenCI_Type_A_MOD_C_ADD_D(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_LERP_B_C:        // = (A-B)*C+B  can mapped to LERP(arg1,arg2,arg0)
                                    //              or mapped to BLENDALPHA(arg1,arg2) if C is
                                    //              alpha channel or DIF, TEX, FAC, CUR
                n=GenCI_Type_A_LERP_B_C(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_SUB_B_ADD_D:     // = A-B+C      can not map very well in 1 stage
                n=GenCI_Type_A_SUB_B_ADD_D(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_SUB_B_MOD_C:     // = (A-B)*C    can not map very well in 1 stage
                n=GenCI_Type_A_SUB_B_MOD_C(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_ADD_B_MOD_C:
                n=GenCI_Type_A_ADD_B_MOD_C(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_B_C_A:
                n=GenCI_Type_A_B_C_A(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
            case CM_FMT_TYPE_A_B_C_D:       // = (A-B)*C+D  can not map very well in 1 stage
                n=GenCI_Type_A_B_C_D(j*2+i, n, gci);
                if( j==0 && mux.splitType[i+2] != CM_FMT_TYPE_NOT_USED ) NextStage(n);  else n++;
                break;
             default:
               break;
            }
        }
        stages[i] = n;
    }

    gci.nStages = max(stages[0], stages[1]);
    if( gci.nStages > m_dwGeneralMaxStages )
    {
        resultIsGood = false;
        gci.nStages = m_dwGeneralMaxStages;
    }

    if( mux.m_ColorTextureFlag[0] != 0 || mux.m_ColorTextureFlag[1] != 0  )
    {
        resultIsGood = false;
    }

    // The bResultIsGoodWithinStages is for Semi-Pixel shader combiner, don't move the code down
    gci.bResultIsGoodWithinStages = resultIsGood;
    if( mux.HowManyConstFactors() > 1 || gci.specularPostOp != MUX_0 || gci.blendingFunc != ENABLE_BOTH )
    {
        gci.bResultIsGoodWithinStages = false;
    }

    if( gci.nStages > stages[0] )   // Color has less stages
    {
        for( int i=stages[0]; i<gci.nStages; i++ )
        {
            gci.stages[i].colorOp.op = CM_REPLACE;
            gci.stages[i].colorOp.Arg1 = MUX_COMBINED;
            gci.stages[i].colorOp.Arg2 = CM_IGNORE;
            gci.stages[i].colorOp.Arg0 = CM_IGNORE;
        }
    }

    if( gci.nStages > stages[1] )   // Color has less stages
    {
        for( int i=stages[1]; i<gci.nStages; i++ )
        {
            gci.stages[i].alphaOp.op = CM_REPLACE;
            gci.stages[i].alphaOp.Arg1 = MUX_COMBINED;
            gci.stages[i].alphaOp.Arg2 = CM_IGNORE;
            gci.stages[i].alphaOp.Arg0 = CM_IGNORE;
        }
    }

    for( int i=0;i<gci.nStages;i++)
    {
        gci.stages[i].bTextureUsed = IsTextureUsedInStage(gci.stages[i]);
    }

    if( !resultIsGood && gci.nStages >= m_dwGeneralMaxStages )
    {
        extern int noOfTwoStages;
        extern GeneralCombinerInfo twostages[];

        for( int k=0; k<noOfTwoStages; k++ )
        {
            GeneralCombinerInfo &info = twostages[k];
            if( (mux.m_dwMux0 == info.dwMux0 && mux.m_dwMux1 == info.dwMux1) ||
                (info.dwMux0+info.dwMux1 == 0 && info.muxDWords[0] == mux.m_dWords[0] && 
                info.muxDWords[1] == mux.m_dWords[1] && info.muxDWords[2] == mux.m_dWords[2] && 
                info.muxDWords[3] == mux.m_dWords[3] && info.m_dwShadeAlphaChannelFlag == mux.m_dwShadeAlphaChannelFlag &&
                info.m_dwShadeColorChannelFlag == mux.m_dwShadeColorChannelFlag ) )
            {
                memcpy(&gci, &info, sizeof(GeneralCombinerInfo) );
                resultIsGood = true;
                break;
            }
        }
    }

#ifdef DEBUGGER
    if( !resultIsGood )
    {
        DecodedMux &mux = *(*m_ppGeneralDecodedMux);
        // Generated combiner mode is not good enough within the limited stages
        DebuggerAppendMsg("\n/*");
        mux.DisplayMuxString("Overflowed");
        mux.DisplaySimpliedMuxString("Overflowed");
        DebuggerAppendMsg("Generated combiners:");
        General_DisplayBlendingStageInfo(gci);
        DebuggerAppendMsg("*/\n");
        DebuggerAppendMsg("\n\n");
        DebuggerAppendMsg("{\n\t0x%08X, 0x%08X, 0x%08X, 0x%08X,\t// Simplified mux\n\t0x%08X, 0x%08X,\t\t// 64bit Mux\n",
            mux.m_dWords[0],mux.m_dWords[1],mux.m_dWords[2],mux.m_dWords[3],mux.m_dwMux0,mux.m_dwMux1);
        DebuggerAppendMsg("\t%d,\t// number of stages\n\tENABLE_BOTH,\n\t0,\t\t// Constant color\n\t0x%08X, 0x%08X, 0,\t// Shade and specular color flags\n\t0x%08X, 0x%08X,\t// constant color texture flags\n",
            2,mux.m_dwShadeColorChannelFlag, mux.m_dwShadeAlphaChannelFlag,mux.m_ColorTextureFlag[0],mux.m_ColorTextureFlag[1]);
        DebuggerAppendMsg("\t{\n\t\t{MOD(T0,DIF), MOD(T0,DIF), 0, true},    // Stage 0\n");
        DebuggerAppendMsg("\t\t{MOD(T0,DIF), SKIP, 1, true},    // Stage 1\n\t}\n},");
    }
#else
    if( !resultIsGood )
    {
        FILE *fp=NULL;
        fp = fopen("C:\\rice\\RiceVideoMUX.log","a");
        if( fp )
        {
            fprintf(fp,"\n/*\n");
            mux.LogMuxString("Overflowed",fp);
            fprintf(fp,"\n\n");
            mux.LogSimpliedMuxString("Overflowed",fp);
            fprintf(fp,"Generated combiners:");
            //General_DisplayBlendingStageInfo(gci);
            fprintf(fp,"\n*/\n");
            fprintf(fp,"\n");
            fprintf(fp,"{\n\t0x%08X, 0x%08X, 0x%08X, 0x%08X,\t// Simplified mux\n\t0x%08X, 0x%08X,\t\t// 64bit Mux\n",
                mux.m_dWords[0],mux.m_dWords[1],mux.m_dWords[2],mux.m_dWords[3],mux.m_dwMux0,mux.m_dwMux1);
            fprintf(fp,"\t%d,\t// number of stages\n\tENABLE_BOTH,\n\tMUX_ENV,\t\t// Constant color\n\t0x%08X, 0x%08X, 0,\t// Shade and specular color flags\n\t0x%08X, 0x%08X,\t// constant color texture flags\n",
                2,mux.m_dwShadeColorChannelFlag, mux.m_dwShadeAlphaChannelFlag,mux.m_ColorTextureFlag[0],mux.m_ColorTextureFlag[1]);
            fprintf(fp,"\t{\n\t\t{MOD(T0,DIF), MOD(T0,DIF), 0, true},   // Stage 0\n");
            fprintf(fp,"\t\t{LERP(T1,CUR,DIF), SKIP, 1, true},  // Stage 1\n\t}\n},");

            fclose(fp);
        }
    }
#endif

    return SaveParserResult(gci);
}


bool CGeneralCombiner::IsTextureUsedInStage(GeneralCombineStage &stage)
{
    if( (stage.colorOp.Arg1&MUX_MASK)==MUX_TEXEL0 || (stage.colorOp.Arg2&MUX_MASK)==MUX_TEXEL0 || (stage.colorOp.Arg0 &MUX_MASK)==MUX_TEXEL0 ||
        (stage.alphaOp.Arg1&MUX_MASK)==MUX_TEXEL0 || (stage.alphaOp.Arg2&MUX_MASK)==MUX_TEXEL0 || (stage.alphaOp.Arg0 &MUX_MASK)==MUX_TEXEL0 ||
        (stage.colorOp.Arg1&MUX_MASK)==MUX_TEXEL1 || (stage.colorOp.Arg2&MUX_MASK)==MUX_TEXEL1 || (stage.colorOp.Arg0 &MUX_MASK)==MUX_TEXEL1 ||
        (stage.alphaOp.Arg1&MUX_MASK)==MUX_TEXEL1 || (stage.alphaOp.Arg2&MUX_MASK)==MUX_TEXEL1 || (stage.alphaOp.Arg0 &MUX_MASK)==MUX_TEXEL1 )
    {
        return true;
    }
    else
        return false;
}


int CGeneralCombiner::SaveParserResult(GeneralCombinerInfo &result)
{
    result.muxDWords[0] = (*m_ppGeneralDecodedMux)->m_dWords[0];
    result.muxDWords[1] = (*m_ppGeneralDecodedMux)->m_dWords[1];
    result.muxDWords[2] = (*m_ppGeneralDecodedMux)->m_dWords[2];
    result.muxDWords[3] = (*m_ppGeneralDecodedMux)->m_dWords[3];
    result.m_dwShadeAlphaChannelFlag = (*m_ppGeneralDecodedMux)->m_dwShadeAlphaChannelFlag;
    result.m_dwShadeColorChannelFlag = (*m_ppGeneralDecodedMux)->m_dwShadeColorChannelFlag;
    result.colorTextureFlag[0] = (*m_ppGeneralDecodedMux)->m_ColorTextureFlag[0];
    result.colorTextureFlag[1] = (*m_ppGeneralDecodedMux)->m_ColorTextureFlag[1];
    result.dwMux0 = (*m_ppGeneralDecodedMux)->m_dwMux0;
    result.dwMux1 = (*m_ppGeneralDecodedMux)->m_dwMux1;

    m_vCompiledCombinerStages.push_back(result);
    m_lastGeneralIndex = m_vCompiledCombinerStages.size()-1;

    return m_lastGeneralIndex;
}


int CGeneralCombiner::FindCompiledMux( )
{
#ifdef DEBUGGER
    if( debuggerDropCombiners || debuggerDropGeneralCombiners )
    {
        m_vCompiledCombinerStages.clear();
        //m_dwLastMux0 = m_dwLastMux1 = 0;
        debuggerDropCombiners = false;
        debuggerDropGeneralCombiners = false;
    }
#endif

    for( uint32 i=0; i<m_vCompiledCombinerStages.size(); i++ )
    {
        if( m_vCompiledCombinerStages[i].dwMux0 == (*m_ppGeneralDecodedMux)->m_dwMux0 && m_vCompiledCombinerStages[i].dwMux1 == (*m_ppGeneralDecodedMux)->m_dwMux1 )
        {
            m_lastGeneralIndex = i;
            return i;
        }
    }

    return -1;
}



bool LM_textureUsedInStage[8];
void CGeneralCombiner::LM_GenCI_Init(GeneralCombinerInfo &gci)
{
    gci.specularPostOp=gci.TFactor=MUX_0;

    gci.blendingFunc = ENABLE_BOTH;

    for( int i=0; i<8; i++)
    {
        gci.stages[i].dwTexture = 0;
        LM_textureUsedInStage[i] = false;
    }
}


//#define fillstage(opr,a1,a2,a3)   {op->op=opr;op->Arg1=a1;op->Arg2=a2;op->Arg0=a3;curStage++;}
inline void FillStage(StageOperate &op, uint32 opr, uint32 a1, uint32 a2, uint32 a3)
{
    op.op = opr;
    op.Arg1 = a1;
    op.Arg2 = a2;
    op.Arg0 = a3;
}

/************************************************************************/
/* New functions, will generate stages within stage limited             */
/* and return the number of stages used.                                */
/************************************************************************/
int CGeneralCombiner::LM_GenCI_Type_D(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    int originalstage=curStage;
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage]))) + channel;
    if( checktexture && LM_Check1TxtrForAlpha(curStage, gci, m.d ) )
    {
        if( limit > 1 )
        {
            FillStage(*op,CM_REPLACE,MUX_COMBINED,CM_IGNORE,CM_IGNORE);
            curStage++;
            op = ((StageOperate*)(&(gci.stages[curStage]))) + channel;
            FillStage(*op,CM_REPLACE,m.d,CM_IGNORE,CM_IGNORE);
        }
        else
        {
            // It is not allowed to use two stages, what to do?
            // It should not happen anyway
            TRACE0("Check me here, at LM_GenCI_Type_D");
        }
    }
    else
    {
        FillStage(*op,CM_REPLACE,m.d,CM_IGNORE,CM_IGNORE);
    }

    gci.stages[curStage].dwTexture = GetTexelNumber(m);
    LM_textureUsedInStage[curStage] = IsTxtrUsed(m);
    curStage++;

    return curStage-originalstage;
}
int CGeneralCombiner::LM_GenCI_Type_A_MOD_C(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci, uint32 dxop)
{
    int originalstage=curStage;
    StageOperate *op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;

    int numberOfTex = CountTexel1Cycle(m);

    if( numberOfTex == 2 )
    {
        // As we can not use both texture in one stage
        // we split them to two stages
        // Stage1: SELECT   txt1
        // Stage2: MOD      txt2

        if( checktexture )
        {
            if( LM_Check1TxtrForAlpha(curStage, gci, m.a ) )
            {
                FillStage(*op,CM_REPLACE,m.c,CM_IGNORE,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.c);
                LM_textureUsedInStage[curStage] = true;
                curStage++;

                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                FillStage(*op,dxop,m.a,MUX_COMBINED,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.a);
                LM_textureUsedInStage[curStage] = true;
                curStage++;
            }
            else
            {
                FillStage(*op,CM_REPLACE,m.a,CM_IGNORE,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.a);
                LM_textureUsedInStage[curStage] = true;
                curStage++;

                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                FillStage(*op,dxop,m.c,MUX_COMBINED,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.c);
                LM_textureUsedInStage[curStage] = true;
                curStage++;
            }
        }
        else
        {
            FillStage(*op,CM_REPLACE,m.a,CM_IGNORE,CM_IGNORE);
            gci.stages[curStage].dwTexture = toTex(m.a);
            LM_textureUsedInStage[curStage] = true;
            curStage++;

            op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
            FillStage(*op,dxop,m.c,MUX_COMBINED,CM_IGNORE);
            gci.stages[curStage].dwTexture = toTex(m.c);
            LM_textureUsedInStage[curStage] = true;
            curStage++;
        }
    }
    else if( numberOfTex == 1)
    {
        if( checktexture )
        {
            if( isTex(m.a) )
            {
                if( LM_Check1TxtrForAlpha(curStage, gci, m.a ) )
                {
                    FillStage(*op,CM_REPLACE,m.c,CM_IGNORE,CM_IGNORE);
                    curStage++;

                    op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                    FillStage(*op,dxop,m.a,MUX_COMBINED,CM_IGNORE);
                    gci.stages[curStage].dwTexture = toTex(m.a);
                    LM_textureUsedInStage[curStage] = true;
                    curStage++;
                }
                else
                {
                    FillStage(*op,CM_REPLACE,m.a,CM_IGNORE,CM_IGNORE);
                    gci.stages[curStage].dwTexture = toTex(m.a);
                    LM_textureUsedInStage[curStage] = true;
                    curStage++;

                    op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                    FillStage(*op,dxop,m.c,MUX_COMBINED,CM_IGNORE);
                    curStage++;
                }
            }
            else
            {
                if( LM_Check1TxtrForAlpha(curStage, gci, m.c ) )
                {
                    FillStage(*op,CM_REPLACE,m.a,CM_IGNORE,CM_IGNORE);
                    curStage++;

                    op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                    FillStage(*op,dxop,m.c,MUX_COMBINED,CM_IGNORE);
                    gci.stages[curStage].dwTexture = toTex(m.c);
                    LM_textureUsedInStage[curStage] = true;
                    curStage++;
                }
                else
                {
                    FillStage(*op,CM_REPLACE,m.c,CM_IGNORE,CM_IGNORE);
                    gci.stages[curStage].dwTexture = toTex(m.c);
                    LM_textureUsedInStage[curStage] = true;
                    curStage++;

                    op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                    FillStage(*op,dxop,m.a,MUX_COMBINED,CM_IGNORE);
                    curStage++;
                }
            }
        }
        else
        {
            if( isTex(m.a) )
            {
                FillStage(*op,CM_REPLACE,m.a,CM_IGNORE,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.a);
                LM_textureUsedInStage[curStage] = true;
                curStage++;

                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                FillStage(*op,dxop,m.c,MUX_COMBINED,CM_IGNORE);
                curStage++;
            }
            else
            {
                FillStage(*op,CM_REPLACE,m.c,CM_IGNORE,CM_IGNORE);
                gci.stages[curStage].dwTexture = toTex(m.c);
                LM_textureUsedInStage[curStage] = true;
                curStage++;

                op = ((StageOperate*)(&(gci.stages[curStage].colorOp))) + channel;
                FillStage(*op,dxop,m.a,MUX_COMBINED,CM_IGNORE);
                curStage++;
            }

        }
    }
    else
    {
        FillStage(*op,dxop,m.a,m.c,CM_IGNORE);
        curStage++;
    }

    return curStage-originalstage;
}
int CGeneralCombiner::LM_GenCI_Type_A_ADD_D(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_SUB_B(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_LERP_B_C(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_MOD_C_ADD_D(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_SUB_B_ADD_D(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_SUB_B_MOD_C(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_ADD_B_MOD_C(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_B_C_D(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}
int CGeneralCombiner::LM_GenCI_Type_A_B_C_A(N64CombinerType &m, int curStage, int limit, int channel, bool checktexture, GeneralCombinerInfo &gci)
{
    return 0;
}

int CGeneralCombiner::LM_ParseDecodedMux()
{
    return 0;
}

bool CGeneralCombiner::LM_Check1TxtrForAlpha(int curStage, GeneralCombinerInfo &gci, uint32 val )
{
    return !( isTex(val) && LM_textureUsedInStage[curStage] && gci.stages[curStage].dwTexture != (unsigned int)toTex(val) );
}


void CGeneralCombiner::LM_SkipStage(StageOperate &op)
{
    op.op = CM_REPLACE;
    op.Arg1 = MUX_COMBINED;
    op.Arg2 = CM_IGNORE;
    op.Arg0 = CM_IGNORE;
}

