Example #1
0
        public static List <Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset)
        {
            var   chunks        = new List <Chunk>();
            var   currentChunk  = new Chunk();
            Chunk previousChunk = null;

            while (offset < endOffset)
            {
                var vifCommand = fileData[offset + 3] & 0x7f;
                var numCommand = fileData[offset + 2] & 0xff;
                int immCommand = DataUtil.getLEShort(fileData, offset);
                switch (vifCommand)
                {
                case NOP_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("NOP");
                    offset += 4;
                    break;

                case STCYCL_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("STCYCL: WL: " + (immCommand >> 8) + " CL: " + (immCommand & 0xFF));
                    offset += 4;
                    break;

                case ITOP_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("ITOP: " + immCommand);
                    offset += 4;
                    break;

                case STMOD_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("STMOD: " + immCommand);
                    offset += 4;
                    break;

                case MSCAL_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("MSCAL: " + immCommand);
                    if (immCommand != 66 && immCommand != 68 && immCommand != 70)
                    {
                        DebugWriteLine("**** Microcode " + immCommand + " not supported");
                    }
                    currentChunk.mscalID = immCommand;
                    chunks.Add(currentChunk);
                    previousChunk = currentChunk;
                    currentChunk  = new Chunk();

                    offset += 4;
                    break;

                case STMASK_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    offset += 4;
                    var stmask = DataUtil.getLEInt(fileData, offset);
                    DebugWriteLine("STMASK: " + stmask);
                    offset += 4;
                    break;

                case FLUSH_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("FLUSH");
                    offset += 4;
                    break;

                case DIRECT_CMD:
                    DebugWrite(HexUtil.formatHex(offset) + " ");
                    DebugWriteLine("DIRECT, " + immCommand * 16 + " bytes");

                    var tags = new GIFTag[immCommand];

                    for (var i = 0; i < immCommand; i++)
                    {
                        tags[i] = new GIFTag();
                        tags[i].parse(fileData, offset + 4 + i * 16);
                    }
                    currentChunk.DIRECTGifTags.AddRange(tags);

                    offset += 4;
                    offset += immCommand * 16;
                    break;

                default:
                    if ((vifCommand & 0x60) == 0x60)
                    {
                        // unpack command
                        var mask = ((vifCommand & 0x10) == 0x10);
                        var vn   = (vifCommand >> 2) & 3;
                        var vl   = vifCommand & 3;
                        var addr = immCommand & 0x1ff;
                        var flag = (immCommand & 0x8000) == 0x8000;
                        var usn  = (immCommand & 0x4000) == 0x4000;

                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        var debugMsg = "UNPACK: vn: " + vn + ", vl: " + vl + ", Addr: " + addr + ", num: " + numCommand;

                        if (flag)
                        {
                            debugMsg += ", Flag";
                        }
                        if (usn)
                        {
                            debugMsg += ", Unsigned";
                        }
                        if (mask)
                        {
                            debugMsg += ", Mask";
                        }
                        DebugWriteLine(debugMsg);
                        offset += 4;
                        if (vn == 1 && vl == 1)
                        {
                            // v2-16
                            // I don't know why but the UVs come after the MSCAL instruction.
                            if (previousChunk != null)
                            {
                                for (var uvnum = 0; uvnum < numCommand; ++uvnum)
                                {
                                    var u = DataUtil.getLEShort(fileData, offset);
                                    var v = DataUtil.getLEShort(fileData, offset + 2);
                                    previousChunk.uvs.Add(new UV(u, v));
                                    offset += 4;
                                }
                            }
                            else
                            {
                                var numBytes = numCommand * 4;
                                offset += numBytes;
                            }
                        }
                        else if (vn == 2 && vl == 1)
                        {
                            // v3-16
                            // each vertex is 128 bits, so num is the number of vertices
                            for (var vnum = 0; vnum < numCommand; ++vnum)
                            {
                                if (!usn)
                                {
                                    var x = DataUtil.getLEShort(fileData, offset);
                                    var y = DataUtil.getLEShort(fileData, offset + 2);
                                    var z = DataUtil.getLEShort(fileData, offset + 4);
                                    offset += 6;

                                    var vertex = new Vertex
                                    {
                                        x = x,
                                        y = y,
                                        z = z
                                    };
                                    currentChunk.vertices.Add(vertex);
                                }
                                else
                                {
                                    int x = DataUtil.getLEUShort(fileData, offset);
                                    int y = DataUtil.getLEUShort(fileData, offset + 2);
                                    int z = DataUtil.getLEUShort(fileData, offset + 4);
                                    offset += 6;

                                    var vloc = new VLoc
                                    {
                                        v1 = x,
                                        v2 = y,
                                        v3 = z
                                    };
                                    currentChunk.vlocs.Add(vloc);
                                }
                            }
                            offset = (offset + 3) & ~3;
                        }
                        else if (vn == 2 && vl == 2)
                        {
                            // v3-8
                            var idx = offset;
                            for (var vnum = 0; vnum < numCommand; ++vnum)
                            {
                                var vec = new SByteVector
                                {
                                    x = (sbyte)fileData[idx++],
                                    y = (sbyte)fileData[idx++],
                                    z = (sbyte)fileData[idx++]
                                };
                                currentChunk.normals.Add(vec);
                            }
                            var numBytes = ((numCommand * 3) + 3) & ~3;
                            offset += numBytes;
                        }
                        else if (vn == 3 && vl == 0)
                        {
                            // v4-32
                            log.LogLine("v4-32 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                            if (1 == numCommand)
                            {
                                currentChunk.gifTag0 = new GIFTag();
                                currentChunk.gifTag0.parse(fileData, offset);
                                DebugWrite(HexUtil.formatHex(offset) + " ");
                                DebugWriteLine("GifTag: " + currentChunk.gifTag0.ToString());
                            }
                            else if (2 == numCommand)
                            {
                                currentChunk.gifTag0 = new GIFTag();
                                currentChunk.gifTag0.parse(fileData, offset);
                                currentChunk.gifTag1 = new GIFTag();
                                currentChunk.gifTag1.parse(fileData, offset + 16);

                                DebugWrite(HexUtil.formatHex(offset) + " ");
                                DebugWriteLine("GifTag0: " + currentChunk.gifTag0.ToString());
                                DebugWrite(HexUtil.formatHex(offset) + " ");
                                DebugWriteLine("GifTag1: " + currentChunk.gifTag1.ToString());
                            }
                            else
                            {
                                log.LogLine("unknown number of gif commands.");
                            }
                            var numBytes = numCommand * 16;
                            offset += numBytes;
                        }
                        else if (vn == 3 && vl == 1)
                        {
                            // v4-16
                            log.LogLine("v4-16 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                            var numShorts = numCommand * 4;
                            if (usn)
                            {
                                currentChunk.extraVlocs = new ushort[numShorts];
                                for (var i = 0; i < numCommand; ++i)
                                {
                                    currentChunk.extraVlocs[i * 4]     = DataUtil.getLEUShort(fileData, offset + i * 8);
                                    currentChunk.extraVlocs[i * 4 + 1] = DataUtil.getLEUShort(fileData, offset + i * 8 + 2);
                                    currentChunk.extraVlocs[i * 4 + 2] = DataUtil.getLEUShort(fileData, offset + i * 8 + 4);
                                    currentChunk.extraVlocs[i * 4 + 3] = DataUtil.getLEUShort(fileData, offset + i * 8 + 6);
                                }
                            }
                            else
                            {
                                log.LogLine("Unsupported tag");
                            }
                            offset += numShorts * 2;
                        }
                        else if (vn == 3 && vl == 2)
                        {
                            // v4-8
                            var numBytes = numCommand * 4;
                            currentChunk.vertexWeights = new List <VertexWeight>();
                            var curVertex = 0;
                            for (var i = 0; i < numCommand; ++i)
                            {
                                var vw = new VertexWeight
                                {
                                    startVertex = curVertex,
                                    bone1       = fileData[offset++] / 4,
                                    boneWeight1 = fileData[offset++],
                                    bone2       = fileData[offset++]
                                };
                                if (vw.bone2 == 0xFF)
                                {
                                    // Single bone
                                    vw.boneWeight2 = 0;
                                    int count = fileData[offset++];
                                    curVertex += count;
                                }
                                else
                                {
                                    vw.bone2      /= 4;
                                    vw.boneWeight2 = fileData[offset++];
                                    ++curVertex;

                                    if (vw.boneWeight1 + vw.boneWeight2 < 255)
                                    {
                                        ++i;
                                        vw.bone3       = fileData[offset++] / 4;
                                        vw.boneWeight3 = fileData[offset++];
                                        vw.bone4       = fileData[offset++];
                                        int bw4 = fileData[offset++];
                                        if (vw.bone4 != 255)
                                        {
                                            vw.bone4      /= 4;
                                            vw.boneWeight4 = bw4;
                                        }
                                    }
                                }
                                vw.endVertex = curVertex - 1;
                                currentChunk.vertexWeights.Add(vw);
                            }
                        }
                        else
                        {
                            DebugWriteLine("Unknown vnvl combination: vn=" + vn + ", vl=" + vl);
                            offset = endOffset;
                        }
                    }
                    else
                    {
                        DebugWriteLine("Unknown command: " + vifCommand);
                        offset = endOffset;
                    }
                    break;
                }
            }
            return(chunks);
        }
Example #2
0
        public static WriteableBitmap Decode(byte[] data, int startOffset, int length)
        {
            int endIndex = startOffset + length;
            int finalw   = BitConverter.ToInt16(data, startOffset);
            int finalh   = BitConverter.ToInt16(data, startOffset + 2);

            int sourcew = finalw;
            int sourceh = finalh;

            PalEntry[] pixels = null;

            int    curIdx = 0x80 + startOffset;
            GIFTag gifTag = new GIFTag();

            gifTag.parse(data, curIdx);

            // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool,
            // we can safely make some assumptions about their structure.
            if (gifTag.nloop == 4)
            {
                int palw = DataUtil.getLEShort(data, curIdx + 0x30);
                int palh = DataUtil.getLEShort(data, curIdx + 0x34);

                curIdx += 0x50;
                GIFTag gifTag2 = new GIFTag();
                gifTag2.parse(data, curIdx);

                // 8 bit palletised
                PalEntry[] palette = PalEntry.readPalette(data, curIdx + 0x10, palw, palh);

                palette = PalEntry.unswizzlePalette(palette);

                int palLen = palw * palh * 4;
                curIdx += (palLen + 0x10);

                GIFTag gifTag50 = new GIFTag();
                gifTag50.parse(data, curIdx);
                curIdx += 0x20;

                int dbw = (sourcew / 2 + 0x07) & ~0x07;
                int dbh = (sourceh / 2 + 0x07) & ~0x07;

                // The following should be a loop, there are repeating sections
                while (curIdx < endIndex - 0x10)
                {
                    GIFTag gifTag3 = new GIFTag();
                    gifTag3.parse(data, curIdx);

                    int dimOffset = 0x10;

                    int thisRrw = DataUtil.getLEShort(data, curIdx + dimOffset);
                    int thisRrh = DataUtil.getLEShort(data, curIdx + dimOffset + 4);

                    int startx = DataUtil.getLEShort(data, curIdx + dimOffset + 20);
                    int starty = DataUtil.getLEShort(data, curIdx + dimOffset + 22);

                    curIdx += gifTag.nloop * 0x10 + 0x10;
                    pixels  = readPixels32(pixels, data, palette, curIdx, startx, starty, thisRrw, thisRrh, dbw, dbh);
                    curIdx += thisRrw * thisRrh * 4;
                }
                if (palLen != 64)
                {
                    pixels  = unswizzle8bpp(pixels, dbw * 2, dbh * 2);
                    sourcew = dbw * 2;
                    sourceh = dbh * 2;
                }
                else
                {
                    sourcew = dbw;
                    sourceh = dbh;
                }
            }
            else if (gifTag.nloop == 3)
            {
                GIFTag gifTag2 = new GIFTag();
                gifTag2.parse(data, startOffset + 0xC0);

                if (gifTag2.flg == 2)
                {
                    // image mode
                    pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh);
                }
            }
            WriteableBitmap image = null;

            if (finalw != 0 && pixels != null)
            {
                image = new WriteableBitmap(
                    finalw, finalh,
                    96, 96,
                    PixelFormats.Bgr32,
                    null);
                image.Lock();
                unsafe {
                    IntPtr pBackBuffer = image.BackBuffer;
                    for (int y = 0; y < sourceh; ++y)
                    {
                        for (int x = 0; x < sourcew; ++x)
                        {
                            PalEntry pixel = pixels[y * sourcew + x];
                            if (pixel != null)
                            {
                                if (x < finalw && y < finalh)
                                {
                                    var p = pBackBuffer + y * image.BackBufferStride + x * 4;
                                    *((int *)p) = pixel.argb();
                                }
                            }
                        }
                    }
                }
                // Specify the area of the bitmap that changed.
                image.AddDirtyRect(new Int32Rect(0, 0, finalw, finalh));

                // Release the back buffer and make it available for display.
                image.Unlock();
            }
            return(image);
        }
Example #3
0
        public static WriteableBitmap Decode(byte[] data, int startOffset, int length)
        {
            int endIndex = startOffset + length;
            int finalw = BitConverter.ToInt16(data, startOffset);
            int finalh = BitConverter.ToInt16(data, startOffset + 2);

            int sourcew = finalw;
            int sourceh = finalh;
            PalEntry[] pixels = null;

            int curIdx = 0x80 + startOffset;
            GIFTag gifTag = new GIFTag();
            gifTag.parse(data, curIdx);

            // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool,
            // we can safely make some assumptions about their structure.
            if (gifTag.nloop == 4) {

                int palw = DataUtil.getLEShort(data, curIdx + 0x30);
                int palh = DataUtil.getLEShort(data, curIdx + 0x34);

                curIdx += 0x50;
                GIFTag gifTag2 = new GIFTag();
                gifTag2.parse(data, curIdx);

                // 8 bit palletised
                PalEntry[] palette = PalEntry.readPalette(data, curIdx + 0x10, palw, palh);

                palette = PalEntry.unswizzlePalette(palette);

                int palLen = palw * palh * 4;
                curIdx += (palLen + 0x10);

                GIFTag gifTag50 = new GIFTag();
                gifTag50.parse(data, curIdx);
                curIdx += 0x20;

                int dbw = (sourcew / 2 + 0x07) & ~0x07;
                int dbh = (sourceh / 2 + 0x07) & ~0x07;

                // The following should be a loop, there are repeating sections
                while (curIdx < endIndex - 0x10) {
                    GIFTag gifTag3 = new GIFTag();
                    gifTag3.parse(data, curIdx);

                    int dimOffset = 0x10;

                    int thisRrw = DataUtil.getLEShort(data, curIdx + dimOffset);
                    int thisRrh = DataUtil.getLEShort(data, curIdx + dimOffset + 4);

                    int startx = DataUtil.getLEShort(data, curIdx + dimOffset + 20);
                    int starty = DataUtil.getLEShort(data, curIdx + dimOffset + 22);

                    curIdx += gifTag.nloop * 0x10 + 0x10;
                    pixels = readPixels32(pixels, data, palette, curIdx, startx, starty, thisRrw, thisRrh, dbw, dbh);
                    curIdx += thisRrw * thisRrh * 4;
                }
                if (palLen != 64) {
                    pixels = unswizzle8bpp(pixels, dbw * 2, dbh * 2);
                    sourcew = dbw * 2;
                    sourceh = dbh * 2;
                } else {
                    sourcew = dbw;
                    sourceh = dbh;
                }

            } else if (gifTag.nloop == 3) {
                GIFTag gifTag2 = new GIFTag();
                gifTag2.parse(data, startOffset + 0xC0);

                if (gifTag2.flg == 2) {
                    // image mode
                    pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh);
                }
            }
            WriteableBitmap image = null;
            if (finalw != 0 && pixels != null) {
                image = new WriteableBitmap(
                    finalw, finalh,
                    96, 96,
                    PixelFormats.Bgr32,
                    null);
                image.Lock();
                unsafe {
                    IntPtr pBackBuffer = image.BackBuffer;
                    for (int y = 0; y < sourceh; ++y) {
                        for (int x = 0; x < sourcew; ++x) {
                            PalEntry pixel = pixels[y * sourcew + x];
                            if (pixel != null) {
                                if (x < finalw && y < finalh) {
                                    var p = pBackBuffer + y * image.BackBufferStride + x * 4;
                                    *((int*)p) = pixel.argb();
                                }
                            }
                        }
                    }
                }
                // Specify the area of the bitmap that changed.
                image.AddDirtyRect(new Int32Rect(0, 0, finalw, finalh));

                // Release the back buffer and make it available for display.
                image.Unlock();
            }
            return image;
        }
Example #4
0
        public static List<Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset)
        {
            var chunks = new List<Chunk>();
            Chunk currentChunk = new Chunk();
            Chunk previousChunk = null;
            while (offset < endOffset) {
                int vifCommand = fileData[offset + 3] & 0x7f;
                int numCommand = fileData[offset + 2] & 0xff;
                int immCommand = DataUtil.getLEShort(fileData, offset);
                switch (vifCommand) {
                    case NOP_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("NOP");
                        offset += 4;
                        break;
                    case STCYCL_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("STCYCL: WL: " + (immCommand >> 8) + " CL: " + (immCommand & 0xFF));
                        offset += 4;
                        break;
                    case ITOP_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("ITOP: " + immCommand);
                        offset += 4;
                        break;
                    case STMOD_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("STMOD: " + immCommand);
                        offset += 4;
                        break;
                    case MSCAL_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("MSCAL: " + immCommand);
                        if (immCommand != 66 && immCommand != 68 && immCommand != 70)
                        {
                            DebugWriteLine("**** Microcode " + immCommand + " not supported");
                        }
                        currentChunk.mscalID = immCommand;
                        chunks.Add(currentChunk);
                        previousChunk = currentChunk;
                        currentChunk = new Chunk();

                        offset += 4;
                        break;
                    case STMASK_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        offset += 4;
                        int stmask = DataUtil.getLEInt(fileData, offset);
                        DebugWriteLine("STMASK: " + stmask);
                        offset += 4;
                        break;
                    case FLUSH_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("FLUSH");
                        offset += 4;
                        break;
                    case DIRECT_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("DIRECT, " + immCommand*16 + " bytes");

                        GIFTag[] tags = new GIFTag[immCommand];

                        for (int i = 0; i < immCommand; i++)
                        {
                            tags[i] = new GIFTag();
                            tags[i].parse(fileData, offset + 4 + i*16);
                        }
                        currentChunk.DIRECTGifTags.AddRange(tags);

                        offset += 4;
                        offset += immCommand * 16;
                        break;
                    default:
                        if ((vifCommand & 0x60) == 0x60) {
                            // unpack command
                            bool mask = ((vifCommand & 0x10) == 0x10);
                            int vn = (vifCommand >> 2) & 3;
                            int vl = vifCommand & 3;
                            int addr = immCommand & 0x1ff;
                            bool flag = (immCommand & 0x8000) == 0x8000;
                            bool usn = (immCommand & 0x4000) == 0x4000;

                            DebugWrite(HexUtil.formatHex(offset) + " ");
                            String debugMsg = "UNPACK: vn: " + vn + ", vl: " + vl + ", Addr: " + addr + ", num: " + numCommand;

                            if (flag) {
                                debugMsg += ", Flag";
                            }
                            if (usn) {
                                debugMsg += ", Unsigned";
                            }
                            if (mask) {
                                debugMsg += ", Mask";
                            }
                            DebugWriteLine(debugMsg);
                            offset += 4;
                            if (vn == 1 && vl == 1) {
                                // v2-16
                                // I don't know why but the UVs come after the MSCAL instruction.
                                if (previousChunk != null) {
                                    for (int uvnum = 0; uvnum < numCommand; ++uvnum) {
                                        short u = DataUtil.getLEShort(fileData, offset);
                                        short v = DataUtil.getLEShort(fileData, offset + 2);
                                        previousChunk.uvs.Add(new UV(u, v));
                                        offset += 4;
                                    }
                                } else {
                                    int numBytes = numCommand * 4;
                                    offset += numBytes;
                                }
                            } else if (vn == 2 && vl == 1) {
                                // v3-16
                                // each vertex is 128 bits, so num is the number of vertices
                                for (int vnum = 0; vnum < numCommand; ++vnum) {
                                    if (!usn) {
                                        short x = DataUtil.getLEShort(fileData, offset);
                                        short y = DataUtil.getLEShort(fileData, offset + 2);
                                        short z = DataUtil.getLEShort(fileData, offset + 4);
                                        offset += 6;

                                        Vertex vertex = new Vertex();
                                        vertex.x = x;
                                        vertex.y = y;
                                        vertex.z = z;
                                        currentChunk.vertices.Add(vertex);
                                    } else {
                                        int x = DataUtil.getLEUShort(fileData, offset);
                                        int y = DataUtil.getLEUShort(fileData, offset + 2);
                                        int z = DataUtil.getLEUShort(fileData, offset + 4);
                                        offset += 6;

                                        VLoc vloc = new VLoc();
                                        vloc.v1 = x;
                                        vloc.v2 = y;
                                        vloc.v3 = z;
                                        currentChunk.vlocs.Add(vloc);
                                    }
                                }
                                offset = (offset + 3) & ~3;
                            } else if (vn == 2 && vl == 2) {
                                // v3-8
                                int idx = offset;
                                for (int vnum = 0; vnum < numCommand; ++vnum) {
                                    SByteVector vec = new SByteVector();
                                    vec.x = (sbyte)fileData[idx++];
                                    vec.y = (sbyte)fileData[idx++];
                                    vec.z = (sbyte)fileData[idx++];
                                    currentChunk.normals.Add(vec);
                                }
                                int numBytes = ((numCommand * 3) + 3) & ~3;
                                offset += numBytes;
                            } else if (vn == 3 && vl == 0) {
                                // v4-32
                                log.LogLine("v4-32 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                                if (1 == numCommand) {
                                    currentChunk.gifTag0 = new GIFTag();
                                    currentChunk.gifTag0.parse(fileData, offset);
                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag: " + currentChunk.gifTag0.ToString());
                                } else if (2 == numCommand) {
                                    currentChunk.gifTag0 = new GIFTag();
                                    currentChunk.gifTag0.parse(fileData, offset);
                                    currentChunk.gifTag1 = new GIFTag();
                                    currentChunk.gifTag1.parse(fileData, offset + 16);

                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag0: " + currentChunk.gifTag0.ToString());
                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag1: " + currentChunk.gifTag1.ToString());
                                } else {
                                    log.LogLine("unknown number of gif commands.");
                                }
                                int numBytes = numCommand * 16;
                                offset += numBytes;
                            } else if (vn == 3 && vl == 1) {
                                // v4-16
                                log.LogLine("v4-16 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                                int numShorts = numCommand * 4;
                                if (usn) {
                                    currentChunk.extraVlocs = new ushort[numShorts];
                                    for (int i = 0; i < numCommand; ++i) {
                                        currentChunk.extraVlocs[i*4] = DataUtil.getLEUShort(fileData, offset + i * 8);
                                        currentChunk.extraVlocs[i * 4 + 1] = DataUtil.getLEUShort(fileData, offset + i * 8 + 2);
                                        currentChunk.extraVlocs[i * 4 + 2] = DataUtil.getLEUShort(fileData, offset + i * 8 + 4);
                                        currentChunk.extraVlocs[i * 4 + 3] = DataUtil.getLEUShort(fileData, offset + i * 8 + 6);
                                    }
                                } else {
                                    log.LogLine("Unsupported tag");
                                }
                                offset += numShorts * 2;
                            } else if (vn == 3 && vl == 2) {
                                // v4-8
                                int numBytes = numCommand * 4;
                                currentChunk.vertexWeights = new List<VertexWeight>();
                                int curVertex=0;
                                for (int i = 0; i < numCommand; ++i) {
                                    VertexWeight vw = new VertexWeight();
                                    vw.startVertex = curVertex;
                                    vw.bone1 = fileData[offset++] / 4;
                                    vw.boneWeight1 = fileData[offset++];
                                    vw.bone2 = fileData[offset++];
                                    if (vw.bone2 == 0xFF) {
                                        // Single bone
                                        vw.boneWeight2 = 0;
                                        int count = fileData[offset++];
                                        curVertex += count;
                                    } else {
                                        vw.bone2 /= 4;
                                        vw.boneWeight2 = fileData[offset++];
                                        ++curVertex;

                                        if (vw.boneWeight1 + vw.boneWeight2 < 255)
                                        {
                                            ++i;
                                            vw.bone3 = fileData[offset++] / 4;
                                            vw.boneWeight3 = fileData[offset++];
                                            vw.bone4 = fileData[offset++];
                                            int bw4 = fileData[offset++];
                                            if (vw.bone4 != 255)
                                            {
                                                vw.bone4 /= 4;
                                                vw.boneWeight4 = bw4;
                                            }
                                        }

                                    }
                                    vw.endVertex = curVertex - 1;
                                    currentChunk.vertexWeights.Add(vw);
                                }

                            } else {
                                DebugWriteLine("Unknown vnvl combination: vn=" + vn + ", vl=" + vl);
                                offset = endOffset;
                            }
                        } else {
                            DebugWriteLine("Unknown command: " + vifCommand);
                            offset = endOffset;
                        }
                        break;
                }
            }
            return chunks;
        }
Example #5
0
        public static WriteableBitmap Decode(byte[] data, int startOffset)
        {
            var gsMem = new GSMemory();

            var length = DataUtil.getLEShort(data, startOffset + 6) * 16;

            int finalw      = BitConverter.ToInt16(data, startOffset);
            int finalh      = BitConverter.ToInt16(data, startOffset + 2);
            var offsetToGIF = DataUtil.getLEInt(data, startOffset + 16);

            var sourcew = finalw;
            var sourceh = finalh;

            PalEntry[] pixels = null;
            byte[]     bytes  = null;

            var curIdx   = offsetToGIF + startOffset;
            var endIndex = curIdx + length;

            var gifTag = new GIFTag();

            gifTag.parse(data, curIdx);

            // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool,
            // we can safely make some assumptions about their structure.
            if (gifTag.nloop == 4)
            {
                int palw = DataUtil.getLEShort(data, curIdx + 0x30);
                int palh = DataUtil.getLEShort(data, curIdx + 0x34);

                curIdx += gifTag.Length;
                var gifTag2 = new GIFTag();
                gifTag2.parse(data, curIdx);

                // 8 bit palletised
                var palette = PalEntry.readPalette(data, curIdx + GIFTag.Size, palw, palh);

                palette = PalEntry.unswizzlePalette(palette);

                curIdx += gifTag2.Length;
                var destWBytes = (finalw + 0x0f) & ~0x0f;
                var destHBytes = (finalh + 0x0f) & ~0x0f;

                var dpsm   = PSMCT32;
                var dbw    = 0;
                var dbp    = 0;
                var rrw    = 0;
                var rrh    = 0;
                var startx = 0;
                var starty = 0;

                while (curIdx < endIndex - GIFTag.Size)
                {
                    var gifTag3 = new GIFTag();
                    gifTag3.parse(data, curIdx);
                    while (!gifTag3.IsImage)
                    {
                        var trxregOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXREG);
                        if (trxregOffset != 0)
                        {
                            rrw = DataUtil.getLEShort(data, trxregOffset);
                            rrh = DataUtil.getLEShort(data, trxregOffset + 4);
                        }
                        var trxposOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXPOS);
                        if (trxposOffset != 0)
                        {
                            startx = DataUtil.getLEShort(data, trxposOffset + 0x04) & 0x07FF;
                            starty = DataUtil.getLEShort(data, trxposOffset + 0x06) & 0x07FF;
                        }
                        var bitbltOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, BITBLTBUF);
                        if (bitbltOffset != 0)
                        {
                            //int sbw = fileData[bitbltOffset + 0x02] & 0x3F;
                            dbp  = data[bitbltOffset + 0x04] & 0x3FFF;
                            dbw  = data[bitbltOffset + 0x06] & 0x3F;
                            dpsm = data[bitbltOffset + 0x07] & 0x3F;
                        }

                        curIdx += gifTag3.Length;
                        if (curIdx + GIFTag.Size >= endIndex)
                        {
                            break;
                        }

                        gifTag3.parse(data, curIdx);
                    }
                    curIdx += GIFTag.Size;     // image gif tag
                    var bytesToTransfer = gifTag3.nloop * 16;

                    if (palette.Length == 16)
                    {
                        // source is PSMT4. Dest can be PSMT4 or PSMCT32
                        if (dpsm == PSMCT32)
                        {
                            var imageData    = data;
                            var imageDataIdx = curIdx;
                            // check for multiple IMAGE entries.
                            var nextTagInd = bytesToTransfer + curIdx;
                            if (nextTagInd < endIndex - GIFTag.Size)
                            {
                                var imageTag2 = new GIFTag();
                                imageTag2.parse(data, nextTagInd);
                                if (imageTag2.flg == 2)
                                {
                                    // IMAGE
                                    var bytesToTransfer2 = imageTag2.nloop * 16;
                                    imageDataIdx = 0;
                                    imageData    = new byte[bytesToTransfer + bytesToTransfer2];
                                    var j = curIdx;
                                    for (var i = 0; i < bytesToTransfer; ++i)
                                    {
                                        imageData[i] = data[j];
                                    }
                                    j = nextTagInd + GIFTag.Size;
                                    for (var i = bytesToTransfer; i < bytesToTransfer + bytesToTransfer2; ++i)
                                    {
                                        imageData[i] = data[j];
                                    }
                                    bytesToTransfer += imageTag2.Length;
                                }
                            }

                            gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, imageData, imageDataIdx);

                            destWBytes = (finalw + 0x3f) & ~0x3f;
                            bytes      = gsMem.readTexPSMT4(dbp, destWBytes / 0x40, startx, starty, destWBytes, destHBytes);
                            bytes      = expand4bit(bytes);
                        }
                        else
                        {
                            // dest and source are the same and so image isn't swizzled
                            bytes = transferPSMT4(bytes, data, curIdx, startx, starty, rrw, rrh, destWBytes, destHBytes);
                        }
                    }
                    else
                    {
                        // source is PSMT8. Dest is always PSMCT32.
                        gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, data, curIdx);
                    }
                    curIdx += bytesToTransfer;
                }
                if (palette.Length == 256)
                {
                    destWBytes = (finalw + 0x3f) & ~0x3f;
                    dbw        = destWBytes / 0x40;
                    bytes      = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh);
                }
                // THIS IS A HACK
                if (palette.Length == 1024)
                {
                    destWBytes = (finalw + 0x3f) & ~0x3f;
                    dbw        = destWBytes / 0x40;
                    bytes      = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh);
                }
                pixels  = applyPalette(palette, bytes);
                sourcew = destWBytes;
                sourceh = destHBytes;
            }
            else if (gifTag.nloop == 3)
            {
                var gifTag2 = new GIFTag();
                gifTag2.parse(data, startOffset + 0xC0);

                if (gifTag2.flg == 2)
                {
                    // image mode
                    pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh);
                }
            }
            WriteableBitmap image = null;

            if (finalw != 0)
            {
                image = new WriteableBitmap(
                    finalw, finalh,
                    96, 96,
                    PixelFormats.Bgra32,
                    null);
                var imageBytes = new int[finalw * finalh];
                var stride     = 4 * finalw;
                var pixOffset  = 0;
                if (pixels != null)
                {
                    for (var y = 0; y < sourceh && y < finalh; ++y)
                    {
                        for (var x = 0; x < sourcew && x < finalw; ++x)
                        {
                            var pixel = pixels[y * sourcew + x];
                            if (pixel != null)
                            {
                                if (x < finalw && y < finalh)
                                {
                                    imageBytes[pixOffset++] = pixel.argb();
                                }
                            }
                        }
                    }
                }
                image.WritePixels(new Int32Rect(0, 0, finalw, finalh), imageBytes, stride, 0);
            }
            return(image);
        }