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); }
private void decodeBlock(int xblock, int yblock, int blockDataStart, int table0Start, WriteableBitmap image, PalEntry[] palette, HuffVal[] huffVals) { int tableOffset = table0Start + 0x800; int table1Len = DataUtil.getLEInt(fileData, tableOffset) * 2; int table1Start = tableOffset + 4; int table2Start = table1Start + table1Len; int table3Start = table2Start + 0x48; int[] pix8s = new int[16 * 16]; int curpix8 = 0; int startBit = 0; int prevPixel = 0; for (int y = 0; y < 16; ++y) { for (int x = 0; x < 16; ++x) { int startWordIdx = startBit / 16; int word1 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2); int word2 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2 + 2); // if startBit is 0, word == word1 // if startBit is 1, word is 15 bits of word1 and 1 bit of word2 int word = ((word1 << 16 | word2) >> (16 - (startBit & 0x0f))) & 0xFFFF; int byte1 = (word >> 8) & 0xff; HuffVal hv = huffVals[byte1]; int pixCmd; if (hv.numBits != 0) { pixCmd = hv.val; startBit += hv.numBits; } else { // Must be more than an 8 bit code int bit = 9; int a = word >> (16 - bit); int v = DataUtil.getLEInt(fileData, table3Start + bit * 4); while (v < a) { ++bit; if (bit > 16) { throw new Exception("A decoding error occured"); } a = word >> (16 - bit); v = DataUtil.getLEInt(fileData, table3Start + bit * 4); } startBit += bit; int val = DataUtil.getLEInt(fileData, table2Start + bit * 4); int table1Index = a + val; pixCmd = DataUtil.getLEShort(fileData, table1Start + table1Index * 2); } int pix8 = 0; if (pixCmd < 0x100) { pix8 = pixCmd; } else if (pixCmd < 0x105) { int backjump = backJumpTable[pixCmd - 0x100]; if ((curpix8 + backjump) >= 0) { pix8 = pix8s[curpix8 + backjump]; } else { throw new Exception("Something went wrong"); } } else { int table0Index = (pixCmd - 0x105) + prevPixel * 8; pix8 = fileData[table0Start + table0Index] & 0xFF; } pix8s[curpix8++] = pix8; prevPixel = pix8 & 0xFF; PalEntry pixel = palette[pix8 & 0xFF]; var pBackBuffer = image.BackBuffer; int xpos = xblock * 16 + x; int ypos = yblock * 16 + y; var p = pBackBuffer + ypos * image.BackBufferStride + xpos * 4; unsafe { *((int *)p) = pixel.argb(); } } } }