/// <summary> /// Decodes a PICA200 Texture. /// </summary> /// <param name="data">Buffer with the Texture</param> /// <param name="width">Width of the Texture</param> /// <param name="height">Height of the Texture</param> /// <param name="format">Pixel Format of the Texture</param> /// <returns></returns> public static Bitmap decode(byte[] data, int width, int height, RenderBase.OTextureFormat format) { byte[] output = new byte[width * height * 4]; long dataOffset = 0; bool toggle = false; switch (format) { case RenderBase.OTextureFormat.rgba8: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + ((tY * 8 + y) * width)) * 4; Buffer.BlockCopy(data, (int)dataOffset + 1, output, (int)outputOffset, 3); output[outputOffset + 3] = data[dataOffset]; dataOffset += 4; } } } break; case RenderBase.OTextureFormat.rgb8: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; Buffer.BlockCopy(data, (int)dataOffset, output, (int)outputOffset, 3); output[outputOffset + 3] = 0xff; dataOffset += 3; } } } break; case RenderBase.OTextureFormat.rgba5551: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; ushort pixelData = (ushort)(data[dataOffset] | (data[dataOffset + 1] << 8)); byte r = (byte)(((pixelData >> 1) & 0x1f) << 3); byte g = (byte)(((pixelData >> 6) & 0x1f) << 3); byte b = (byte)(((pixelData >> 11) & 0x1f) << 3); byte a = (byte)((pixelData & 1) * 0xff); output[outputOffset] = (byte)(r | (r >> 5)); output[outputOffset + 1] = (byte)(g | (g >> 5)); output[outputOffset + 2] = (byte)(b | (b >> 5)); output[outputOffset + 3] = a; dataOffset += 2; } } } break; case RenderBase.OTextureFormat.rgb565: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; ushort pixelData = (ushort)(data[dataOffset] | (data[dataOffset + 1] << 8)); byte r = (byte)((pixelData & 0x1f) << 3); byte g = (byte)(((pixelData >> 5) & 0x3f) << 2); byte b = (byte)(((pixelData >> 11) & 0x1f) << 3); output[outputOffset] = (byte)(r | (r >> 5)); output[outputOffset + 1] = (byte)(g | (g >> 6)); output[outputOffset + 2] = (byte)(b | (b >> 5)); output[outputOffset + 3] = 0xff; dataOffset += 2; } } } break; case RenderBase.OTextureFormat.rgba4: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; ushort pixelData = (ushort)(data[dataOffset] | (data[dataOffset + 1] << 8)); byte r = (byte)((pixelData >> 4) & 0xf); byte g = (byte)((pixelData >> 8) & 0xf); byte b = (byte)((pixelData >> 12) & 0xf); byte a = (byte)(pixelData & 0xf); output[outputOffset] = (byte)(r | (r << 4)); output[outputOffset + 1] = (byte)(g | (g << 4)); output[outputOffset + 2] = (byte)(b | (b << 4)); output[outputOffset + 3] = (byte)(a | (a << 4)); dataOffset += 2; } } } break; case RenderBase.OTextureFormat.la8: case RenderBase.OTextureFormat.hilo8: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; output[outputOffset] = data[dataOffset]; output[outputOffset + 1] = data[dataOffset]; output[outputOffset + 2] = data[dataOffset]; output[outputOffset + 3] = data[dataOffset + 1]; dataOffset += 2; } } } break; case RenderBase.OTextureFormat.l8: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; output[outputOffset] = data[dataOffset]; output[outputOffset + 1] = data[dataOffset]; output[outputOffset + 2] = data[dataOffset]; output[outputOffset + 3] = 0xff; dataOffset++; } } } break; case RenderBase.OTextureFormat.a8: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; output[outputOffset] = 0xff; output[outputOffset + 1] = 0xff; output[outputOffset + 2] = 0xff; output[outputOffset + 3] = data[dataOffset]; dataOffset++; } } } break; case RenderBase.OTextureFormat.la4: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; output[outputOffset] = (byte)(data[dataOffset] >> 4); output[outputOffset + 1] = (byte)(data[dataOffset] >> 4); output[outputOffset + 2] = (byte)(data[dataOffset] >> 4); output[outputOffset + 3] = (byte)(data[dataOffset] & 0xf); dataOffset++; } } } break; case RenderBase.OTextureFormat.l4: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; byte c = toggle ? (byte)((data[dataOffset++] & 0xf0) >> 4) : (byte)(data[dataOffset] & 0xf); toggle = !toggle; c = (byte)((c << 4) | c); output[outputOffset] = c; output[outputOffset + 1] = c; output[outputOffset + 2] = c; output[outputOffset + 3] = 0xff; } } } break; case RenderBase.OTextureFormat.a4: for (int tY = 0; tY < height / 8; tY++) { for (int tX = 0; tX < width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long outputOffset = ((tX * 8) + x + (((tY * 8 + y)) * width)) * 4; output[outputOffset] = 0xff; output[outputOffset + 1] = 0xff; output[outputOffset + 2] = 0xff; byte a = toggle ? (byte)((data[dataOffset++] & 0xf0) >> 4) : (byte)(data[dataOffset] & 0xf); toggle = !toggle; output[outputOffset + 3] = (byte)((a << 4) | a); } } } break; case RenderBase.OTextureFormat.etc1: case RenderBase.OTextureFormat.etc1a4: byte[] decodedData = etc1Decode(data, width, height, format == RenderBase.OTextureFormat.etc1a4); int[] etc1Order = etc1Scramble(width, height); int i = 0; for (int tY = 0; tY < height / 4; tY++) { for (int tX = 0; tX < width / 4; tX++) { int TX = etc1Order[i] % (width / 4); int TY = (etc1Order[i] - TX) / (width / 4); for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { dataOffset = ((TX * 4) + x + (((TY * 4) + y) * width)) * 4; long outputOffset = ((tX * 4) + x + (((tY * 4 + y)) * width)) * 4; Buffer.BlockCopy(decodedData, (int)dataOffset, output, (int)outputOffset, 4); } } i += 1; } } break; } return(TextureUtils.getBitmap(output.ToArray(), width, height)); }