Esempio n. 1
0
        public static void PrintDisplaylist(byte[] rdram, ulong address, ulong[] segTable, ref int totalTris, ref int totalVertices, string indentation = "")
        {
            List <F3DEX2Command> cmds = GetDLCommands(rdram, address, segTable);

            for (int i = 0; i < cmds.Count; i++)
            {
                F3DEX2Command cmd = cmds[i];

                string cmdStr = cmd.ParseString(cmds.GetRange(i + 1, Math.Min((cmds.Count() - i - 1), 10)), out int cmdsSkipped, ref totalTris, ref totalVertices, indentation + "                    "); // TODO replace getrange with something of constant time

                if (cmdStr.StartsWith("gs"))
                {
                    Console.Write(string.Format("{0:X16}: ", cmd.Words));
                    Console.WriteLine(indentation + cmdStr + ",");
                }
                else
                {
                    Console.WriteLine(indentation + cmd.CommandId);
                    Console.WriteLine(" " + indentation + cmd + ",");
                }
                if (cmd.CommandId == F3DEX2CommandId.G_DL && (cmd.Words >> (32 + 16) & 0xFF) != 0x01)
                {
                    PrintDisplaylist(rdram, cmd.Words & 0xFFFFFFFF, segTable, ref totalTris, ref totalVertices, "  " + indentation);
                }
                else if (cmd.CommandId == F3DEX2CommandId.G_MOVEWORD)
                {
                    MoveWord mw = new MoveWord(cmd.Words);
                    if (mw.Index == MoveWordIndex.G_MW_SEGMENT)
                    {
                        segTable[mw.Offset / 4] = mw.Data;
                    }
                }

                i += cmdsSkipped;
            }
        }
Esempio n. 2
0
        public string ToString(List <F3DEX2Command> nextCommands, out int commandsSkipped)
        {
            MoveWord[] nextMoveWords = new MoveWord[nextCommands.Count];
            int        numMoveWords;

            commandsSkipped = 0;
            for (numMoveWords = 0; numMoveWords < nextMoveWords.Length; numMoveWords++)
            {
                if (nextCommands[numMoveWords] != null && nextCommands[numMoveWords].CommandId == F3DEX2CommandId.G_MOVEWORD)
                {
                    nextMoveWords[numMoveWords] = new MoveWord(nextCommands[numMoveWords].Words);
                }
                else
                {
                    break;
                }
            }
            switch (Index)
            {
            case MoveWordIndex.G_MW_MATRIX:
                break;     // Not supported in F3DEX2

            case MoveWordIndex.G_MW_NUMLIGHT:
                return(string.Format("gsSPNumLights(NUMLIGHTS_{0})", Data / 24));

            case MoveWordIndex.G_MW_CLIP:
                if (numMoveWords >= 3)
                {
                    if (Offset == (int)MoveWordOffsetClip.G_MWO_CLIP_RNX &&
                        nextMoveWords[0].Index == MoveWordIndex.G_MW_CLIP && nextMoveWords[0].Offset == (int)MoveWordOffsetClip.G_MWO_CLIP_RNY &&
                        nextMoveWords[1].Index == MoveWordIndex.G_MW_CLIP && nextMoveWords[1].Offset == (int)MoveWordOffsetClip.G_MWO_CLIP_RPX &&
                        nextMoveWords[2].Index == MoveWordIndex.G_MW_CLIP && nextMoveWords[2].Offset == (int)MoveWordOffsetClip.G_MWO_CLIP_RPY)
                    {
                        if (Data >= 1 && Data <= 6 &&                             // Check that it's a valid ratio
                            Data == nextMoveWords[0].Data &&                      // Check that the first two are the same negative ratio
                            nextMoveWords[1].Data == nextMoveWords[2].Data &&     // Check that the second two are the same positive ratio
                            Data + nextMoveWords[1].Data == 0x10000)              // Check that the first two and second two are in the same ratio pair
                        {
                            commandsSkipped = 3;
                            return(string.Format("gsSPClipRatio({0})", ((FrustumRatio)Data).ToString().Substring(7)));
                        }
                    }
                }
                break;

            case MoveWordIndex.G_MW_SEGMENT:
                return(string.Format("gsSPSegment(0x{0:X2}, 0x{1:X8})", Offset / 4, Data));

            case MoveWordIndex.G_MW_FOG:
                return(string.Format("gsSPFogFactor({0}, {1})", (int)(Data >> 16) & 0xFFFF, (int)Data & 0xFFFF));

            case MoveWordIndex.G_MW_LIGHTCOL:     // TODO test this
                if (numMoveWords >= 1)
                {
                    if (nextMoveWords[0].Index == MoveWordIndex.G_MW_LIGHTCOL &&            // Check if the next is also G_MW_LIGHTCOL
                        (Offset % 8) == 0 && nextMoveWords[0].Offset == (Offset + 4) &&     // Check if this is G_MWO_aLIGHT_x and if the next is G_MWO_bLIGHT_x
                        Data == nextMoveWords[0].Data)                                      // Check if the next has the same color
                    {
                        commandsSkipped = 1;
                        return(string.Format("gsSPLightColor({0}, 0x{1:X8})", ((MoveWordOffsetLightColor)Offset).ToString().Substring(7), Data));
                    }
                }
                break;

            // TODO ForceMtx
            case MoveWordIndex.G_MW_PERSPNORM:
                if (Offset == 0)
                {
                    return(string.Format("gsSPPerspNormalize(0x{0:X4})", Data));
                }
                break;
            }
            return(string.Format("gsMoveWd({0}, {1}, 0x{2:X})", Index, Enum.ToObject(MoveWordEnums[Index], Offset), Data));
        }
Esempio n. 3
0
        public string ParseString(List <F3DEX2Command> nextCmds, out int cmdsSkipped, ref int totalTris, ref int totalVertices, string indentation)
        {
            cmdsSkipped = 0;
            switch (CommandId)
            {
            case F3DEX2CommandId.G_NOOP:
            {
                if ((Words & 0xFFFFFF) == 0)
                {
                    return("gsDPNoOp()");
                }
                else
                {
                    return(string.Format("gsDPNoOpTag({0})", Words & 0xFFFFFF));
                }
            }

            case F3DEX2CommandId.G_RDPHALF_2: break;

            case F3DEX2CommandId.G_SETOTHERMODE_H:
            {
                F3DEX2Decoder.ParseOtherMode(this,
                                             out int sft, out int len, out ulong data);

                OthermodeHShift sftType = (OthermodeHShift)sft;

                switch (sftType)
                {
                case OthermodeHShift.G_MDSFT_ALPHADITHER:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetAlphaDither({0})", (AlphaDither)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_RGBDITHER:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetColorDither({0})", (ColorDither)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_COMBKEY:
                    if (len == 1)
                    {
                        return(string.Format("gsDPSetCombineKey({0})", (CombineKey)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTCONV:
                    if (len == 3)
                    {
                        return(string.Format("gsDPSetTextureConvert({0})", (TextureConvert)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTFILT:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetTextureFilter({0})", (TextureFilter)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTLUT:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetTextureLUT({0})", (TextureLUT)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTLOD:
                    if (len == 1)
                    {
                        return(string.Format("gsDPSetTextureLOD({0})", (TextureLOD)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTDETAIL:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetTextureDetail({0})", (TextureDetail)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_TEXTPERSP:
                    if (len == 1)
                    {
                        return(string.Format("gsDPSetTexturePersp({0})", (TexturePersp)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_CYCLETYPE:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetCycleType({0})", (CycleType)data));
                    }
                    break;

                case OthermodeHShift.G_MDSFT_PIPELINE:
                    if (len == 1)
                    {
                        return(string.Format("gsDPPipelineMode({0})", (PipelineMode)data));
                    }
                    break;
                }
                break;
            }

            case F3DEX2CommandId.G_SETOTHERMODE_L:
            {
                F3DEX2Decoder.ParseOtherMode(this,
                                             out int sft, out int len, out ulong data);

                OthermodeLShift sftType = (OthermodeLShift)sft;

                switch (sftType)
                {
                case OthermodeLShift.G_MDSFT_ALPHACOMPARE:
                    if (len == 2)
                    {
                        return(string.Format("gsDPSetAlphaCompare({0})", (AlphaCompare)data));
                    }
                    break;

                case OthermodeLShift.G_MDSFT_ZSRCSEL:
                    if (len == 1)
                    {
                        return(string.Format("gsDPSetDepthSource({0})", (DepthSource)data));
                    }
                    break;

                case OthermodeLShift.G_MDSFT_RENDERMODE:
                    if (len == 29)
                    {
                        RenderMode rm = new RenderMode(Words);
                        return(string.Format("gsDPSetRenderMode({0})", rm));
                    }
                    break;

                case OthermodeLShift.G_MDSFT_BLENDER:
                    break;
                }

                break;
            }

            case F3DEX2CommandId.G_RDPHALF_1:
            {
                ulong rdpHalfData = Words & 0xFFFFFFFF;

                if (nextCmds.Count >= 1)
                {
                    // gsSPBranchLessZrg/raw
                    if (nextCmds[0].CommandId == F3DEX2CommandId.G_BRANCH_Z)
                    {
                        F3DEX2Decoder.ParseBranchLessZ(nextCmds[0],
                                                       out int vtx, out int zval);

                        cmdsSkipped = 1;

                        return(string.Format("gsSPBranchLessZraw(0x{0:X8}, {1}, {2})", rdpHalfData, vtx, zval));
                    }
                    // gsSPLoadUCodeEx
                    else if (nextCmds[0].CommandId == F3DEX2CommandId.G_LOAD_UCODE)
                    {
                        F3DEX2Decoder.ParseLoadUcode(nextCmds[0],
                                                     out ulong uc_start, out int uc_dsize);

                        cmdsSkipped = 1;

                        return(string.Format("gsSPLoadUcodeEx(0x{0:X8}, 0x{1:X8}, 0x{2:X8})", uc_start, rdpHalfData, uc_dsize));
                    }
                }

                return(string.Format("gImmp1(pkt, G_RDPHALF_1, {0})", rdpHalfData));
            }

            case F3DEX2CommandId.G_SPNOOP:
                return("gsSPNoOp()");

            case F3DEX2CommandId.G_ENDDL:
                return("gsSPEndDisplayList()");

            case F3DEX2CommandId.G_DL:
                if ((Words >> (32 + 16) & 0xFF) == 0x01)
                {
                    return(string.Format("gsSPBranchList(0x{0:X8})", Words & 0xFFFFFFFF));
                }
                else
                {
                    return(string.Format("gsSPDisplayList(0x{0:X8})", Words & 0xFFFFFFFF));
                }

            case F3DEX2CommandId.G_LOAD_UCODE: break;

            case F3DEX2CommandId.G_MOVEMEM:
            {
                MoveMem mm = new MoveMem(Words);
                return(mm.ToString());
            }

            case F3DEX2CommandId.G_MOVEWORD:
            {
                MoveWord mw = new MoveWord(Words);
                return(mw.ToString(nextCmds, out cmdsSkipped));
            }

            case F3DEX2CommandId.G_MTX:
            {
                F3DEX2Decoder.ParseMatrix(this,
                                          out MatrixType mType, out MatrixLoadType loadType, out MatrixPushType pushType, out ulong mAddr,
                                          out int size, out int offset);

                if (size == 64 && offset == 0)         // TODO remove magic number
                {
                    return(string.Format("gsSPMatrix(0x{0:X8}, {1} | {2} | {3})", mAddr, mType, loadType, pushType));
                }
                else
                {
                    return(string.Format("gsDma2p(G_MTX, {0:X8}, {1}, {2} | {3} | {4}, {5}", mAddr, size, mType, loadType, pushType, offset));
                }
            }

            case F3DEX2CommandId.G_GEOMETRYMODE:
            {
                GeometryMode gm = new GeometryMode(Words);
                return(gm.ToString());
            }

            case F3DEX2CommandId.G_POPMTX:
            {
                F3DEX2Decoder.ParsePopMtx(this,
                                          out int bytes, out int idx);

                if (idx == 2)
                {
                    if (bytes == 64)
                    {
                        return("gsSPPopMatrix(G_MTX_MODELVIEW)");
                    }
                    else if (bytes % 64 == 0)
                    {
                        return(string.Format("gsSPPopMatrixN(G_MTX_MODELVIEW, {0})", bytes / 64));
                    }
                }

                break;
            }

            case F3DEX2CommandId.G_TEXTURE:
            {
                F3DEX2Decoder.ParseTexture(this,
                                           out int s, out int t, out int level, out int tile, out int on);

                string tileString;

                if (tile == 7)
                {
                    tileString = "G_TX_LOADTILE";
                }
                else if (tile == 0)
                {
                    tileString = "G_TX_RENDERTILE";
                }
                else
                {
                    tileString = tile.ToString();
                }

                string onString;

                if (on == 1)
                {
                    onString = "G_ON";
                }
                else if (on == 0)
                {
                    onString = "G_OFF";
                }
                else
                {
                    onString = on.ToString();
                }

                return(string.Format("gsSPTexture(0x{0:X4}, 0x{1:X4}, {2}, {3}, {4})", s, t, level, tileString, onString));
            }

            case F3DEX2CommandId.G_DMA_IO: break;

            case F3DEX2CommandId.G_SPECIAL_1:
            case F3DEX2CommandId.G_SPECIAL_2:
            case F3DEX2CommandId.G_SPECIAL_3:
            {
                // TODO This will not work in the general case, and is tailored to F3DEX2 2.04H, which is the only ucode I've seen
                // that uses any G_SPECIAL commands (G_SPECIAL_1 in that case).
                return(string.Format("gsImmp1({0}, {1})", CommandId, Words & 0xFFFFFFFF));
            }

            case F3DEX2CommandId.G_VTX:
            {
                F3DEX2Decoder.ParseVtx(this,
                                       out int vtxCount, out int vtxOffset, out ulong vtxAddr);

                totalVertices += vtxCount;

                return(string.Format("gsSPVertex(0x{0:X8}, {1}, {2})", vtxAddr, vtxCount, vtxOffset));
            }

            case F3DEX2CommandId.G_MODIFYVTX: break;

            case F3DEX2CommandId.G_CULLDL:
            {
                F3DEX2Decoder.ParseCullDL(this,
                                          out int vstart, out int vend);
                return(string.Format("gsSPCullDisplayList({0}, {1})", vstart, vend));
            }

            case F3DEX2CommandId.G_BRANCH_Z: break;

            case F3DEX2CommandId.G_TRI1:
            {
                F3DEX2Decoder.ParseTri(this, true,
                                       out int a, out int b, out int c);

                totalTris++;

                return(string.Format("gsSP1Triangle({0}, {1}, {2}, 0)", a, b, c));
            }

            case F3DEX2CommandId.G_TRI2:
            {
                F3DEX2Decoder.ParseTri(this, true,
                                       out int a1, out int b1, out int c1);
                F3DEX2Decoder.ParseTri(this, false,
                                       out int a2, out int b2, out int c2);

                totalTris += 2;

                return(string.Format("gsSP2Triangles({0}, {1}, {2}, 0, {3}, {4}, {5}, 0)", a1, b1, c1, a2, b2, c2));
            }

            case F3DEX2CommandId.G_QUAD:     // TODO test this
            {
                F3DEX2Decoder.ParseTri(this, true,
                                       out int a1, out int b1, out int c1);
                F3DEX2Decoder.ParseTri(this, false,
                                       out int _, out int _, out int c2);

                return(string.Format("gsSP1Quadrangle({0}, {1}, {2}, {3}, 0)", a1, b1, c1, c2));
            }

            case F3DEX2CommandId.G_LINE3D: break;

            case F3DEX2CommandId.G_SETCIMG:
            {
                F3DEX2Decoder.ParseSetImage(this,
                                            out int fmt, out int siz, out int width, out ulong i);

                return(string.Format("gsDPSetColorImage({0}, {1}, {2}, 0x{3:X8})",
                                     (RDPImgFormat)fmt, (RDPImgSize)siz,
                                     width, i));
            }

            case F3DEX2CommandId.G_SETZIMG:
            {
                F3DEX2Decoder.ParseSetImage(this,
                                            out int fmt, out int siz, out int width, out ulong i);

                if (fmt == 0 && siz == 0 && width == 1)
                {
                    return(string.Format("gsDPSetDepthImage(0x{0:X8})", i));
                }
                else
                {
                    return(string.Format("gsSetImage(G_SETZIMG, {0}, {1}, {2}, 0x{3:X8})", fmt, siz, width, i));
                }
            }

            case F3DEX2CommandId.G_SETTIMG:
            {
                F3DEX2Decoder.ParseSetImage(this,
                                            out int fmt, out int siz, out int width, out ulong i);

                return(string.Format("gsDPSetTextureImage({0}, {1}, {2}, 0x{3:X8})",
                                     (RDPImgFormat)fmt, (RDPImgSize)siz,
                                     width, i));
            }

            case F3DEX2CommandId.G_SETCOMBINE:
            {
                F3DEX2Decoder.ParseSetCombine(this,
                                              out ulong ulongMode);

                CombineMode mode = new CombineMode(ulongMode);

                string modeStr = mode.ToString();

                if (modeStr.StartsWith("G_CC"))
                {
                    return(string.Format("gsDPSetCombine({0})", modeStr));
                }
                else
                {
                    return(string.Format("gsDPSetCombineLerp({0})", modeStr));
                }
            }

            case F3DEX2CommandId.G_SETENVCOLOR:
            {
                F3DEX2Decoder.ParseSetColor(this,
                                            out ulong col);
                F3DEX2Decoder.GetComponents(col,
                                            out int r, out int g, out int b, out int a);

                return(string.Format("gsDPSetEnvColor(0x{0:X2}, 0x{1:X2}, 0x{2:X2}, 0x{3:X2})", r, g, b, a));
            }

            case F3DEX2CommandId.G_SETPRIMCOLOR:
            {
                F3DEX2Decoder.ParseSetColor(this,
                                            out ulong col);
                F3DEX2Decoder.GetComponents(col,
                                            out int r, out int g, out int b, out int a);

                int m = (int)(Words >> (32 + 8)) & 0xFF;
                int l = (int)(Words >> (32 + 0)) & 0xFF;

                return(string.Format("gsDPSetPrimColor(0x{0:X2}, 0x{1:X2}, 0x{2:X2}, 0x{3:X2}, 0x{4:X2}, 0x{5:X2})", m, l, r, g, b, a));
            }

            case F3DEX2CommandId.G_SETBLENDCOLOR:
            {
                F3DEX2Decoder.ParseSetColor(this,
                                            out ulong col);
                F3DEX2Decoder.GetComponents(col,
                                            out int r, out int g, out int b, out int a);

                return(string.Format("gsDPSetBlendColor(0x{0:X2}, 0x{1:X2}, 0x{2:X2}, 0x{3:X2})", r, g, b, a));
            }

            case F3DEX2CommandId.G_SETFOGCOLOR:
            {
                F3DEX2Decoder.ParseSetColor(this,
                                            out ulong col);
                F3DEX2Decoder.GetComponents(col,
                                            out int r, out int g, out int b, out int a);

                return(string.Format("gsDPSetFogColor(0x{0:X2}, 0x{1:X2}, 0x{2:X2}, 0x{3:X2})", r, g, b, a));
            }

            case F3DEX2CommandId.G_SETFILLCOLOR:
            {
                F3DEX2Decoder.ParseSetColor(this,
                                            out ulong col);

                return(string.Format("gsDPSetFillColor({0})", F3DEX2Decoder.PackedColorWordToStr(col)));
            }

            case F3DEX2CommandId.G_FILLRECT:
            {
                F3DEX2Decoder.ParseRect(this,
                                        out int xl, out int yl, out int xh, out int yh, out int _);

                return(string.Format("gsDPFillRectangle({0}, {1}, {2}, {3})",
                                     xl >> 2, yl >> 2, (xh >> 2), (yh >> 2)));
            }

            case F3DEX2CommandId.G_SETTILE:
            {
                F3DEX2Decoder.ParseSetTile(this,
                                           out int fmt, out int siz, out int line, out int tmem, out int tile, out int palette, out int cmt,
                                           out int maskt, out int shiftt, out int cms, out int masks, out int shifts);
                RDPMirror    mirrorT = (RDPMirror)(cmt & 0x1);
                RDPWrapClamp clampT  = (RDPWrapClamp)(cmt & 0x2);
                RDPMirror    mirrorS = (RDPMirror)(cms & 0x1);
                RDPWrapClamp clampS  = (RDPWrapClamp)(cms & 0x2);

                return(string.Format("gsDPSetTile({0}, {1}, {2}, {3}, {4}, {5}, {6}|{7}, {8}, {9}, {10}|{11}, {12}, {13})",
                                     (RDPImgFormat)fmt, (RDPImgSize)siz,
                                     line, tmem, tile, palette,
                                     mirrorT, clampT,
                                     maskt, shiftt,
                                     mirrorS, clampS,
                                     masks, shifts));
            }

            case F3DEX2CommandId.G_LOADTILE:
            {
                F3DEX2Decoder.ParseLoadTileGeneric(this,
                                                   out int tile, out int uls, out int ult, out int lrs, out int lrt);

                string ulsStr = F3DEX2Decoder.TexCoord102ToStr(uls);
                string ultStr = F3DEX2Decoder.TexCoord102ToStr(ult);
                string lrsStr = F3DEX2Decoder.TexCoord102ToStr(lrs, true);
                string lrtStr = F3DEX2Decoder.TexCoord102ToStr(lrt, true);

                return(string.Format("gsDPLoadTile({0}, {1}, {2}, {3}, {4})",
                                     tile, ulsStr, ultStr, lrsStr, lrtStr)); // TODO CI4 textures are loaded as 8b, so this may not produce a clean output
            }

            case F3DEX2CommandId.G_LOADBLOCK:
            {
                F3DEX2Decoder.ParseLoadTileGeneric(this,
                                                   out int tile, out int uls, out int ult, out int lrs, out int dxt);

                return(string.Format("gsDPLoadBlock({0}, 0x{1:X3}, 0x{2:X3}, 0x{3:X3}, 0x{4:X3})",
                                     tile, uls, ult, lrs, dxt));
            }

            case F3DEX2CommandId.G_SETTILESIZE:
            {
                F3DEX2Decoder.ParseLoadTileGeneric(this,
                                                   out int tile, out int uls, out int ult, out int lrs, out int lrt);

                string ulsStr = F3DEX2Decoder.TexCoord102ToStr(uls);
                string ultStr = F3DEX2Decoder.TexCoord102ToStr(ult);
                string lrsStr = F3DEX2Decoder.TexCoord102ToStr(lrs, true);
                string lrtStr = F3DEX2Decoder.TexCoord102ToStr(lrt, true);

                return(string.Format("gsDPSetTileSize({0}, {1}, {2}, {3}, {4})",
                                     tile, ulsStr, ultStr, lrsStr, lrtStr));
            }

            case F3DEX2CommandId.G_LOADTLUT:
            {
                F3DEX2Decoder.ParseLoadTLUT(this,
                                            out int tile, out int count);
                return(string.Format("gsDPLoadTLUTCmd({0}, {1})", tile, count));
            }

            case F3DEX2CommandId.G_RDPSETOTHERMODE:
            {
                F3DEX2Decoder.ParseRDPSetOtherMode(this,
                                                   out PipelineMode pm, out CycleType cyc, out TexturePersp tp, out TextureDetail td, out TextureLOD tl,
                                                   out TextureLUT tt, out TextureFilter tf, out TextureConvert tc, out CombineKey ck, out ColorDither cd,
                                                   out AlphaDither ad, out AlphaCompare ac, out DepthSource zs, out RenderMode rm);

                return(string.Format("gsDPSetOtherMode(\n" +
                                     indentation + "{0} | {1} | {2} | {3} | {4} | {5}\n" +
                                     indentation + "{6} | {7} | {8} | {9} | {10},\n" +
                                     indentation + "{11} | {12} | {13})",
                                     pm, cyc, tp, td, tl, tt, tf, tc, ck, cd, ad,
                                     ac, zs, rm.ToString().Replace(",", " |")));
            }

            case F3DEX2CommandId.G_SETPRIMDEPTH: break;

            case F3DEX2CommandId.G_SETSCISSOR:
            {
                F3DEX2Decoder.ParseScissor(this,
                                           out int mode, out int ulx, out int uly, out int lrx, out int lry);

                return(string.Format("gsDPSetScissor({0}, {1:F2}f, {2:F2}f, {3:F2}f, {4:F2}f)",
                                     (ScissorMode)mode, ulx / 4.0f, uly / 4.0f, lrx / 4.0f, lry / 4.0f));
            }

            case F3DEX2CommandId.G_SETCONVERT:
            {
                F3DEX2Decoder.ParseConvert(this,
                                           out int[] coeffs);

                string k0Str = (coeffs[0] == (short)ConvertCoeff.G_CV_K0) ? "G_CV_K0" : coeffs[0].ToString();
                string k1Str = (coeffs[1] == (short)ConvertCoeff.G_CV_K1) ? "G_CV_K1" : coeffs[1].ToString();
                string k2Str = (coeffs[2] == (short)ConvertCoeff.G_CV_K2) ? "G_CV_K2" : coeffs[2].ToString();
                string k3Str = (coeffs[3] == (short)ConvertCoeff.G_CV_K3) ? "G_CV_K3" : coeffs[3].ToString();
                string k4Str = (coeffs[4] == (short)ConvertCoeff.G_CV_K4) ? "G_CV_K4" : coeffs[4].ToString();
                string k5Str = (coeffs[5] == (short)ConvertCoeff.G_CV_K5) ? "G_CV_K5" : coeffs[5].ToString();

                return(string.Format("gsDPSetConvert({0}, {1}, {2}, {3}, {4}, {5})",
                                     k0Str, k1Str, k2Str, k3Str, k4Str, k5Str));
            }

            case F3DEX2CommandId.G_SETKEYR: break;

            case F3DEX2CommandId.G_SETKEYGB: break;

            case F3DEX2CommandId.G_RDPFULLSYNC:
                return("gsDPFullSync()");

            case F3DEX2CommandId.G_RDPTILESYNC:
                return("gsDPTileSync()");

            case F3DEX2CommandId.G_RDPPIPESYNC:
                return("gsDPPipeSync()");

            case F3DEX2CommandId.G_RDPLOADSYNC:
                return("gsDPLoadSync()");

            case F3DEX2CommandId.G_TEXRECTFLIP:     // Fall through
            case F3DEX2CommandId.G_TEXRECT:
            {
                F3DEX2Decoder.ParseRect(this,
                                        out int xl, out int yl, out int xh, out int yh, out int tile);

                string xlStr = F3DEX2Decoder.TexCoord102ToStr(xl);
                string ylStr = F3DEX2Decoder.TexCoord102ToStr(yl);

                string xhStr = F3DEX2Decoder.TexCoord102OffsetToStr(xl, xh);
                string yhStr = F3DEX2Decoder.TexCoord102OffsetToStr(yl, yh);

                F3DEX2Command nextCmd0 = nextCmds[0];
                F3DEX2Command nextCmd1 = nextCmds[1];

                if (nextCmd0.CommandId == F3DEX2CommandId.G_RDPHALF_1 && nextCmd1.CommandId == F3DEX2CommandId.G_RDPHALF_2)
                {
                    F3DEX2Decoder.ParseRDPHalf_HH(nextCmd0, out int s, out int t);
                    F3DEX2Decoder.ParseRDPHalf_HH(nextCmd1, out int dsdx, out int dtdy);

                    cmdsSkipped = 2;

                    if (CommandId == F3DEX2CommandId.G_TEXRECT)
                    {
                        return(string.Format("gsSPTextureRectangle({0}, {1}, {2}, {3}, {4}, 0x{5:X4}, 0x{6:X4}, 0x{7:X4}, 0x{8:X4})",
                                             xlStr, ylStr, xhStr, yhStr, tile, s, t, dsdx, dtdy));
                    }
                    else
                    {
                        return(string.Format("gsSPTextureRectangleFlip({0}, {1}, {2}, {3}, {4}, 0x{5:X4}, 0x{6:X4}, 0x{7:X4}, 0x{8:X4})",
                                             xlStr, ylStr, xhStr, yhStr, tile, s, t, dsdx, dtdy));
                    }
                }
                break;
            }
            }
            return(string.Format("{0:X8},{1:X8}", Words >> 32, Words & 0xFFFFFFFF));
        }