/// <summary> /// Encodes a PICA200 Texture. /// </summary> /// <param name="img">Input image to be encoded</param> /// <param name="format">Pixel Format of the Texture</param> /// <returns></returns> public byte[] encode(Bitmap img, RenderBase.OTextureFormat format) { byte[] data = new TextureUtils().getArray(img); byte[] output = new byte[data.Length]; uint outputOffset = 0; switch (format) { case RenderBase.OTextureFormat.rgba8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + ((tY * 8 + y) * img.Width)) * 4; Buffer.BlockCopy(data, (int)dataOffset, output, (int)outputOffset + 1, 3); output[outputOffset] = data[dataOffset + 3]; outputOffset += 4; } } } break; default: throw new NotImplementedException(); } return(output); }
private int returnSize(RenderBase.OTextureFormat fmt, int w, int h) { switch (fmt) { case RenderBase.OTextureFormat.rgba8: return((w * h) * 4); case RenderBase.OTextureFormat.rgb8: return((w * h) * 3); case RenderBase.OTextureFormat.rgba5551: return((w * h) * 2); case RenderBase.OTextureFormat.rgb565: return((w * h) * 2); case RenderBase.OTextureFormat.rgba4: return((w * h) * 2); case RenderBase.OTextureFormat.la8: return((w * h) * 2); case RenderBase.OTextureFormat.hilo8: return((w * h) * 2); case RenderBase.OTextureFormat.l8: return(w * h); case RenderBase.OTextureFormat.a8: return(w * h); case RenderBase.OTextureFormat.la4: return(w * h); case RenderBase.OTextureFormat.l4: return((w * h) >> 1); case RenderBase.OTextureFormat.a4: return((w * h) >> 1); case RenderBase.OTextureFormat.etc1: return((w * h) >> 1); case RenderBase.OTextureFormat.etc1a4: return(w * h); default: throw new Exception("OBCHTextureReplacer: Invalid texture format on BCH!"); } }
public static RenderBase.OTexture loadTexture(string fileName) { if (File.Exists(fileName)) { Serialization.SERI tex = Serialization.getSERI(fileName); int width = tex.getIntegerParameter("w"); int height = tex.getIntegerParameter("h"); int mipmap = tex.getIntegerParameter("mipmap"); int format = tex.getIntegerParameter("format"); string textureName = tex.getStringParameter("tex"); string fullTextureName = Path.Combine(Path.GetDirectoryName(fileName), textureName); if (File.Exists(fullTextureName)) { RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare; switch (format) { case 0: fmt = RenderBase.OTextureFormat.l4; break; case 1: fmt = RenderBase.OTextureFormat.l8; break; case 7: fmt = RenderBase.OTextureFormat.rgb565; break; case 8: fmt = RenderBase.OTextureFormat.rgba5551; break; case 9: fmt = RenderBase.OTextureFormat.rgba4; break; case 0xa: fmt = RenderBase.OTextureFormat.rgba8; break; case 0xb: fmt = RenderBase.OTextureFormat.rgb8; break; case 0xc: fmt = RenderBase.OTextureFormat.etc1; break; case 0xd: fmt = RenderBase.OTextureFormat.etc1a4; break; default: Debug.WriteLine("NLP Model: Unknown Texture format 0x" + format.ToString("X8")); break; } string name = Path.GetFileNameWithoutExtension(textureName); byte[] buffer = File.ReadAllBytes(fullTextureName); return(new RenderBase.OTexture(TextureCodec.decode(buffer, width, height, fmt), name)); } } return(null); }
/// <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)); }
public static RenderBase.OTexture loadTexture(string fileName) { if (File.Exists(fileName + ".xml")) { Serialization.SERI tex = Serialization.getSERI(fileName + ".xml"); int width = tex.getIntegerParameter("w"); int height = tex.getIntegerParameter("h"); int mipmap = tex.getIntegerParameter("mipmap"); int format = tex.getIntegerParameter("format"); string textureName = tex.getStringParameter("tex"); string fullTextureName = fileName; if (File.Exists(fullTextureName)) { RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare; switch (format) { case 0: fmt = RenderBase.OTextureFormat.l4; break; case 1: fmt = RenderBase.OTextureFormat.l8; break; case 3: fmt = RenderBase.OTextureFormat.la8; break; case 7: fmt = RenderBase.OTextureFormat.rgb565; break; case 8: fmt = RenderBase.OTextureFormat.rgba5551; break; case 9: fmt = RenderBase.OTextureFormat.rgba4; break; case 0xa: fmt = RenderBase.OTextureFormat.rgba8; break; case 0xb: fmt = RenderBase.OTextureFormat.rgb8; break; case 0xc: fmt = RenderBase.OTextureFormat.etc1; break; case 0xd: fmt = RenderBase.OTextureFormat.etc1a4; break; default: Debug.WriteLine("NLP Model: Unknown Texture format 0x" + format.ToString("X8")); break; } string name = Path.GetFileNameWithoutExtension(textureName); byte[] buffer = File.ReadAllBytes(fullTextureName); if (mipmap > 1) { int lenght = 0; var texture = (fmt == RenderBase.OTextureFormat.etc1 || fmt == RenderBase.OTextureFormat.etc1a4? TextureCodec.decode(buffer, width, height, fmt, out lenght) : TextureCodec.getIMG(buffer, width, height, fmt, out lenght)); List <Bitmap> miptextures = new List <Bitmap>(); int lenghtall = lenght; for (var i = 1; i < mipmap; i++) { var mip = new byte[lenghtall]; Buffer.BlockCopy(buffer, lenghtall, mip, 0, buffer.Length - lenghtall); var miptexture = (fmt == RenderBase.OTextureFormat.etc1 || fmt == RenderBase.OTextureFormat.etc1a4 ? TextureCodec.decode(mip, width / Convert.ToInt32(Math.Pow(2, i)), height / Convert.ToInt32(Math.Pow(2, i)), fmt, out lenght) : TextureCodec.getIMG(mip, width / Convert.ToInt32(Math.Pow(2, i)), height / Convert.ToInt32(Math.Pow(2, i)), fmt, out lenght)); miptextures.Add(miptexture); lenghtall += lenght; } return(new RenderBase.OTexture(texture, miptextures, name)); } else { int dataOffset; //TextureCodec.encode(TextureCodec.decode(buffer, width, height, fmt, out dataOffset), fmt); return(new RenderBase.OTexture(TextureCodec.decode(buffer, width, height, fmt, out dataOffset), name)); } } } return(null); }
public static byte[] EncodeTexture(string fileName) { if (File.Exists(fileName)) { if (File.Exists(fileName.Replace(filetype, ".texi.xml"))) { Serialization.SERI tex = Serialization.getSERI(fileName.Replace(filetype, ".texi.xml")); int width = tex.getIntegerParameter("w"); int height = tex.getIntegerParameter("h"); int mipmap = tex.getIntegerParameter("mipmap"); int format = tex.getIntegerParameter("format"); string textureName = tex.getStringParameter("tex"); //string fullTextureName = Path.Combine(Path.GetDirectoryName(fileName), textureName); RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare; switch (format) { case 0: fmt = RenderBase.OTextureFormat.l4; break; case 1: fmt = RenderBase.OTextureFormat.l8; break; case 3: fmt = RenderBase.OTextureFormat.la8; break; case 7: fmt = RenderBase.OTextureFormat.rgb565; break; case 8: fmt = RenderBase.OTextureFormat.rgba5551; break; case 9: fmt = RenderBase.OTextureFormat.rgba4; break; case 0xa: fmt = RenderBase.OTextureFormat.rgba8; break; case 0xb: fmt = RenderBase.OTextureFormat.rgb8; break; case 0xc: fmt = RenderBase.OTextureFormat.etc1; break; case 0xd: fmt = RenderBase.OTextureFormat.etc1a4; break; default: Debug.WriteLine("NLP Model: Unknown Texture format 0x" + format.ToString("X8")); break; } Stream imageStreamSource = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); PngBitmapDecoder decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); int frame = 0; // adjust/ loop for multiframe tiffs BitmapSource bitmapSource = decoder.Frames[0]; // this piece works for 8-bit grayscale. Adjust for other formats. Bitmap img = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, PixelFormat.Format32bppArgb); BitmapData data = img.LockBits(new Rectangle(System.Drawing.Point.Empty, img.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); img.UnlockBits(data); IEnumerable <byte> main = TextureCodec.getPixelData(img, fmt); if (mipmap > 1) { for (var i = 1; i < mipmap; i++) { var mimefile = fileName.Replace(filetype, ".mip" + (img.Width / Convert.ToInt32(Math.Pow(2, i))).ToString() + "X" + (img.Height / Convert.ToInt32(Math.Pow(2, i))).ToString() + filetype); if (File.Exists(mimefile)) { Bitmap mip = new Bitmap(mimefile); main = main.Concat(TextureCodec.getPixelData(mip, fmt)); mip.Dispose(); } else { Console.WriteLine(Path.GetFileName(mimefile) + " not found"); } } } img.Dispose(); return(main.ToArray()); } } return(null); }
internal static Color DecodeColor(uint val, RenderBase.OTextureFormat format) { int alpha = 0xFF, red, green, blue; switch (format) { case RenderBase.OTextureFormat.l8: // L8 return(Color.FromArgb(alpha, (byte)val, (byte)val, (byte)val)); case RenderBase.OTextureFormat.a8: // A8 return(Color.FromArgb((byte)val, alpha, alpha, alpha)); case RenderBase.OTextureFormat.la4: // LA4 red = (byte)(val >> 4); alpha = (byte)(val & 0x0F); return(Color.FromArgb(alpha, red, red, red)); case RenderBase.OTextureFormat.la8: // LA8 red = (byte)((val >> 8 & 0xFF)); alpha = (byte)(val & 0xFF); return(Color.FromArgb(alpha, red, red, red)); case RenderBase.OTextureFormat.hilo8: // HILO8 red = (byte)(val >> 8); green = (byte)(val & 0xFF); return(Color.FromArgb(alpha, red, green, 0xFF)); case RenderBase.OTextureFormat.rgb565: // RGB565 red = Convert5To8[(val >> 11) & 0x1F]; green = (byte)(((val >> 5) & 0x3F) * 4); blue = Convert5To8[val & 0x1F]; return(Color.FromArgb(alpha, red, green, blue)); case RenderBase.OTextureFormat.rgb8: // RGB8 red = (byte)((val >> 16) & 0xFF); green = (byte)((val >> 8) & 0xFF); blue = (byte)(val & 0xFF); return(Color.FromArgb(alpha, red, green, blue)); case RenderBase.OTextureFormat.rgba5551: // RGBA5551 red = Convert5To8[(val >> 11) & 0x1F]; green = Convert5To8[(val >> 6) & 0x1F]; blue = Convert5To8[(val >> 1) & 0x1F]; alpha = (val & 0x0001) == 1 ? 0xFF : 0x00; return(Color.FromArgb(alpha, red, green, blue)); case RenderBase.OTextureFormat.rgba4: // RGBA4444 alpha = (byte)(0x11 * (val & 0xf)); red = (byte)(0x11 * ((val >> 12) & 0xf)); green = (byte)(0x11 * ((val >> 8) & 0xf)); blue = (byte)(0x11 * ((val >> 4) & 0xf)); return(Color.FromArgb(alpha, red, green, blue)); case RenderBase.OTextureFormat.rgba8: // RGBA8888 red = (byte)((val >> 24) & 0xFF); green = (byte)((val >> 16) & 0xFF); blue = (byte)((val >> 8) & 0xFF); alpha = (byte)(val & 0xFF); return(Color.FromArgb(alpha, red, green, blue)); // case 10: // case 11: case RenderBase.OTextureFormat.l4: // L4 return(Color.FromArgb(alpha, (byte)(val * 0x11), (byte)(val * 0x11), (byte)(val * 0x11))); case RenderBase.OTextureFormat.a4: // A4 return(Color.FromArgb((byte)(val * 0x11), alpha, alpha, alpha)); default: return(Color.White); } }
public static byte[] getPixelData(Bitmap img, RenderBase.OTextureFormat format) { int w = img.Width; int h = img.Height; using (MemoryStream mz = new MemoryStream()) using (BinaryWriter bz = new BinaryWriter(mz)) { int p = gcm(w, 8) / 8; if (p == 0) { p = 1; } for (uint i = 0; i < w * h; i++) { uint x; uint y; d2xy(i % 64, out x, out y); // Get Shift Tile uint tile = i / 64; // Shift Tile Coordinate into Tilemap x += (uint)(tile % p) * 8; y += (uint)(tile / p) * 8; // Don't write data Color c; if (x >= img.Width || y >= img.Height) { c = Color.FromArgb(0, 0, 0, 0); } else { c = img.GetPixel((int)x, (int)y); } switch (format) { case RenderBase.OTextureFormat.l8: bz.Write(GetL8(c)); break; // L8 case RenderBase.OTextureFormat.a8: bz.Write(GetA8(c)); break; // A8 case RenderBase.OTextureFormat.la4: bz.Write(GetLA4(c)); break; // LA4(4) case RenderBase.OTextureFormat.la8: bz.Write(GetLA8(c)); break; // LA8(8) case RenderBase.OTextureFormat.hilo8: bz.Write(GetHILO8(c)); break; // HILO8 case RenderBase.OTextureFormat.rgb565: bz.Write(GetRGB565(c)); break; // RGB565 case RenderBase.OTextureFormat.rgba5551: bz.Write(GetRGBA5551(c)); break; // RGBA5551 case RenderBase.OTextureFormat.rgba4: bz.Write(GetRGBA4444(c)); break; // RGBA4444 case RenderBase.OTextureFormat.rgba8: bz.Write(GetRGBA8888(c)); break; // RGBA8 case RenderBase.OTextureFormat.rgb8: bz.Write(GetRGB8(c)); break; // RGB8 default: throw new Exception("Unsupport format."); } } //while (mz.Length < nlpo2((int)mz.Length)) // pad // bz.Write((byte)0); return(mz.ToArray()); } }
/// <summary> /// Encodes a PICA200 Texture. /// </summary> /// <param name="img">Input image to be encoded</param> /// <param name="format">Pixel Format of the Texture</param> /// <returns></returns> /// public static byte[] encode(Bitmap img, RenderBase.OTextureFormat format) { byte[] data = TextureUtils.getArray(img); byte[] output = new byte[data.Length]; bool toggle = false; uint outputOffset = 0; switch (format) { case RenderBase.OTextureFormat.rgba8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + ((tY * 8 + y) * img.Width)) * 4; Buffer.BlockCopy(data, (int)dataOffset, output, (int)outputOffset + 1, 3); output[outputOffset] = data[dataOffset + 3]; outputOffset += 4; } } } break; case RenderBase.OTextureFormat.rgb8: { output = new byte[img.Width * img.Height * 3]; for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; if (data[dataOffset + 3] != 0xFF) { } Buffer.BlockCopy(data, (int)dataOffset, output, (int)outputOffset, 3); outputOffset += 3; } } } } break; case RenderBase.OTextureFormat.rgba5551: { output = new byte[img.Width * img.Height * 2]; for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; int r = (data[dataOffset + 0] * 31 / 255); int g = (data[dataOffset + 1] * 31 / 255); int b = (data[dataOffset + 2] * 31 / 255); int a = (data[dataOffset + 3] > 0 ? 1 : 0); ushort rShift = (ushort)(r << 1); ushort gShift = (ushort)(g << 6); ushort bShift = (ushort)(b << 11); ushort t = (ushort)(rShift | gShift | bShift | a); output[outputOffset] = (byte)(t); output[outputOffset + 1] = (byte)(t >> 8); outputOffset += 2; } } } } break; default: throw new NotImplementedException(); } return(output); }
internal static Bitmap getIMG(byte[] data, int Width, int Height, RenderBase.OTextureFormat format, out int dataOffset) { dataOffset = 0; Bitmap img = new Bitmap(Width, Height); int area = img.Width * img.Height; // Tiles Per Width int p = gcm(img.Width, 8) / 8; if (p == 0) { p = 1; } using (Stream BitmapStream = new MemoryStream(data)) using (BinaryReader br = new BinaryReader(BitmapStream)) for (uint i = 0; i < area; i++) // for every pixel { uint x; uint y; d2xy(i % 64, out x, out y); uint tile = i / 64; // Shift Tile Coordinate into Tilemap x += (uint)(tile % p) * 8; y += (uint)(tile / p) * 8; // Get Color Color c; switch (format) { case RenderBase.OTextureFormat.l8: // L8 // 8bit/1 byte case RenderBase.OTextureFormat.a8: // A8 case RenderBase.OTextureFormat.la4: // LA4 c = DecodeColor(br.ReadByte(), format); break; case RenderBase.OTextureFormat.la8: // LA8 // 16bit/2 byte case RenderBase.OTextureFormat.hilo8: // HILO8 case RenderBase.OTextureFormat.rgb565: // RGB565 case RenderBase.OTextureFormat.rgba4: // RGBA4444 case RenderBase.OTextureFormat.rgba5551: // RGBA5551 c = DecodeColor(br.ReadUInt16(), format); break; case RenderBase.OTextureFormat.rgb8: // RGB8: // 24bit byte[] data1 = br.ReadBytes(3); Array.Resize(ref data1, 4); c = DecodeColor(BitConverter.ToUInt32(data1, 0), format); break; case RenderBase.OTextureFormat.rgba8: // RGBA8888 c = DecodeColor(br.ReadUInt32(), format); break; case RenderBase.OTextureFormat.l4: // L4 case RenderBase.OTextureFormat.a4: // A4 // 4bit - Do 2 pixels at a time. uint val = br.ReadByte(); img.SetPixel((int)x, (int)y, DecodeColor(val & 0xF, format)); // lowest bits for the low pixel i++; x++; c = DecodeColor(val >> 4, format); // highest bits for the high pixel break; default: throw new Exception("Invalid FileFormat."); } img.SetPixel((int)x, (int)y, c); dataOffset = (int)br.BaseStream.Position; } return(img); }
private loadedTexture loadPKM(FileStream data, BinaryReader input) { loadedTexture tex; tex.modified = false; long descAddress2 = data.Position; data.Seek(descAddress2 + 0x18, SeekOrigin.Begin); int texLength = input.ReadInt32(); data.Seek(descAddress2 + 0x28, SeekOrigin.Begin); string textureName = IOUtils.readStringWithLength(input, 0x40); data.Seek(descAddress2 + 0x68, SeekOrigin.Begin); ushort width = input.ReadUInt16(); ushort height = input.ReadUInt16(); ushort texFormat = input.ReadUInt16(); ushort texMipMaps = input.ReadUInt16(); data.Seek(0x10, SeekOrigin.Current); tex.offset = (uint)data.Position; byte[] texBuffer = input.ReadBytes(texLength); RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare; switch (texFormat) { case 0x2: fmt = RenderBase.OTextureFormat.rgb565; break; case 0x3: fmt = RenderBase.OTextureFormat.rgb8; break; case 0x4: fmt = RenderBase.OTextureFormat.rgba8; break; case 0x17: fmt = RenderBase.OTextureFormat.rgba5551; break; case 0x23: fmt = RenderBase.OTextureFormat.la8; break; case 0x24: fmt = RenderBase.OTextureFormat.hilo8; break; case 0x25: fmt = RenderBase.OTextureFormat.l8; break; case 0x26: fmt = RenderBase.OTextureFormat.a8; break; case 0x27: fmt = RenderBase.OTextureFormat.la4; break; case 0x28: fmt = RenderBase.OTextureFormat.l4; break; case 0x29: fmt = RenderBase.OTextureFormat.a4; break; case 0x2a: fmt = RenderBase.OTextureFormat.etc1; break; case 0x2b: fmt = RenderBase.OTextureFormat.etc1a4; break; } Bitmap texture = TextureCodec.decode(texBuffer, width, height, fmt); tex.texture = new RenderBase.OTexture(texture, textureName); tex.type = fmt; tex.gpuCommandsOffset = 0; tex.gpuCommandsWordCount = 0; tex.length = texLength; return(tex); }
private bool open(string fileName) { using (FileStream data = new FileStream(fileName, FileMode.Open)) { BinaryReader input = new BinaryReader(data); if (peek(input) == 0x00010000) { currentFile = fileName; bch = new loadedBCH(); bch.isBCH = false; bch.mips.Add(new MIPlayer()); packPNK(data, input, bch); } if (peek(input) == 0x15041213) { currentFile = fileName; bch = new loadedBCH(); bch.isBCH = false; bch.mips.Add(new MIPlayer()); bch.mips[0].textures.Add(loadPKM(data, input)); } string magic2b = getMagic(input, 2); if (magic2b == "PC" || magic2b == "CM") { bch = new loadedBCH(); bch.isBCH = false; currentFile = fileName; data.Seek(2, SeekOrigin.Current); ushort numEntrys = input.ReadUInt16(); for (int i = 0; i < (int)numEntrys; i++) { data.Seek(4 + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); uint end = input.ReadUInt32(); uint lenth = end - offset; long rtn = data.Position; data.Seek(offset, SeekOrigin.Begin); if (magic2b == "CM" & i == 0) { packPNK(data, input, bch); } if (lenth > 4) { if (magic2b == "PC") { if (peek(input) == 0x15041213) { bch.mips[0].textures.Add(loadPKM(data, input)); } } } } } string magic = IOUtils.readString(input, 0); if (magic == "BCH") { currentFile = fileName; data.Seek(4, SeekOrigin.Current); byte backwardCompatibility = input.ReadByte(); byte forwardCompatibility = input.ReadByte(); ushort version = input.ReadUInt16(); uint mainHeaderOffset = input.ReadUInt32(); uint stringTableOffset = input.ReadUInt32(); uint gpuCommandsOffset = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); uint dataExtendedOffset = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0; uint relocationTableOffset = input.ReadUInt32(); uint mainHeaderLength = input.ReadUInt32(); uint stringTableLength = input.ReadUInt32(); uint gpuCommandsLength = input.ReadUInt32(); uint dataLength = input.ReadUInt32(); uint dataExtendedLength = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0; uint relocationTableLength = input.ReadUInt32(); data.Seek(mainHeaderOffset, SeekOrigin.Begin); uint modelsPointerTableOffset = input.ReadUInt32() + mainHeaderOffset; uint modelsPointerTableEntries = input.ReadUInt32(); data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin); uint texturesPointerTableOffset = input.ReadUInt32() + mainHeaderOffset; uint texturesPointerTableEntries = input.ReadUInt32(); bch = new loadedBCH(); bch.isBCH = true; for (int i = 0; i < 6; i++) { bch.mips.Add(new MIPlayer()); } MipSelect.Enabled = true; //Textures for (int index = 0; index < texturesPointerTableEntries; index++) { data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin); data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin); loadedTexture tex; tex.modified = false; tex.gpuCommandsOffset = input.ReadUInt32() + gpuCommandsOffset; tex.gpuCommandsWordCount = input.ReadUInt32(); data.Seek(0x14, SeekOrigin.Current); uint textureNameOffset = input.ReadUInt32(); string textureName = IOUtils.readString(input, textureNameOffset + stringTableOffset); data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin); PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount); tex.offset = textureCommands.getTexUnit0Address() + dataOffset; RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format(); Size textureSize = textureCommands.getTexUnit0Size(); tex.type = fmt; int OGW = textureSize.Width; int OGH = textureSize.Height; for (int i = 0; i < 6; i++) { textureSize.Width = OGW / Convert.ToInt32(Math.Pow(2, i)); textureSize.Height = OGH / Convert.ToInt32(Math.Pow(2, i)); tex.length = returnSize(fmt, textureSize.Width, textureSize.Height); if (textureSize.Height >= 8 & textureSize.Width >= 8) { data.Seek(tex.offset, SeekOrigin.Begin); byte[] buffer = new byte[tex.length]; //data.Seek(tex.length + returnSize(fmt, textureSize.Width / 2, textureSize.Height / 2), SeekOrigin.Current); tex.offset = (uint)data.Position; input.Read(buffer, 0, tex.length); Bitmap texture = TextureCodec.decode( buffer, textureSize.Width, textureSize.Height, fmt); tex.texture = new RenderBase.OTexture(texture, textureName); bch.mips[i].textures.Add(tex); tex.offset = (uint)data.Position; } } } bch.mainHeaderOffset = mainHeaderOffset; bch.gpuCommandsOffset = gpuCommandsOffset; bch.dataOffset = dataOffset; bch.relocationTableOffset = relocationTableOffset; bch.relocationTableLength = relocationTableLength; } else if (magic == "CTPK\u0001") { currentFile = fileName; data.Seek(4, SeekOrigin.Current); ushort ver = input.ReadUInt16(); ushort numTexture = input.ReadUInt16(); uint TextureSectionOffset = input.ReadUInt32(); uint TextureSectionSize = input.ReadUInt32(); uint HashSectionOffset = input.ReadUInt32(); uint TextureInfoSection = input.ReadUInt32(); bch = new loadedBCH(); bch.isBCH = false; bch.mips.Add(new MIPlayer()); for (int i = 0; i < numTexture; i++) { data.Seek(0x20 * (i + 1), SeekOrigin.Begin); loadedTexture tex; tex.modified = false; tex.gpuCommandsOffset = (uint)(0x20 * (i + 1)); uint textureNameOffset = input.ReadUInt32(); string textureName = IOUtils.readString(input, textureNameOffset); tex.length = input.ReadInt32(); tex.offset = input.ReadUInt32() + TextureSectionOffset; tex.type = (RenderBase.OTextureFormat)input.ReadUInt32(); ushort Width = input.ReadUInt16(); ushort Height = input.ReadUInt16(); data.Seek(tex.offset, SeekOrigin.Begin); byte[] buffer = new byte[tex.length]; input.Read(buffer, 0, buffer.Length); Bitmap texture = TextureCodec.decode( buffer, Width, Height, tex.type); tex.texture = new RenderBase.OTexture(texture, textureName); tex.gpuCommandsWordCount = 0; bch.mips[0].textures.Add(tex); } } } updateTexturesList(); return(true); }
/// <summary> /// Encodes a PICA200 Texture. /// </summary> /// <param name="img">Input image to be encoded</param> /// <param name="format">Pixel Format of the Texture</param> /// <returns></returns> public static byte[] encode(Bitmap img, RenderBase.OTextureFormat format) { byte[] data = TextureUtils.getArray(img); byte[] output = new byte[data.Length]; bool toggle = false; uint outputOffset = 0; switch (format) { case RenderBase.OTextureFormat.rgba8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + ((tY * 8 + y) * img.Width)) * 4; byte b = (byte)(data[dataOffset]); byte g = (byte)(data[dataOffset + 1]); byte r = (byte)(data[dataOffset + 2]); output[outputOffset + 1] = b; output[outputOffset + 2] = g; output[outputOffset + 3] = r; output[outputOffset] = data[dataOffset + 3]; outputOffset += 4; } } } break; case RenderBase.OTextureFormat.rgb8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + ((tY * 8 + y) * img.Width)) * 4; byte b = (byte)(data[dataOffset]); byte g = (byte)(data[dataOffset + 1]); byte r = (byte)(data[dataOffset + 2]); output[outputOffset] = b; output[outputOffset + 1] = g; output[outputOffset + 2] = r; outputOffset += 3; } } } break; case RenderBase.OTextureFormat.rgba5551: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; ulong pixelData = (ulong)(data[dataOffset]); byte b = (byte)(data[dataOffset]); byte g = (byte)(data[dataOffset + 1]); byte r = (byte)(data[dataOffset + 2]); byte a = (byte)(data[dataOffset + 3]); output[outputOffset] = (byte)((((g >> 3) & 3) << 6) + ((b >> 3) << 1) + (a & 1)); output[outputOffset + 1] = (byte)(((r >> 3) << 3) + (g >> 5)); outputOffset += 2; } } } break; case RenderBase.OTextureFormat.rgb565: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; byte b = (byte)(data[dataOffset] / 8); byte g = (byte)(data[dataOffset + 1] / 4); byte r = (byte)(data[dataOffset + 2] / 8); output[outputOffset] = (byte)(((g & 7) << 5) | b); output[outputOffset + 1] = (byte)((r << 3) | ((g & 0x38) >> 3)); outputOffset += 2; } } } break; case RenderBase.OTextureFormat.rgba4: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; byte b = (byte)(data[dataOffset] / 16); byte g = (byte)(data[dataOffset + 1] / 16); byte r = (byte)(data[dataOffset + 2] / 16); byte a = (byte)(data[dataOffset + 3] / 16); output[outputOffset + 1] = (byte)((r << 4) | g); output[outputOffset] = (byte)((b << 4) | a); outputOffset += 2; } } } break; case RenderBase.OTextureFormat.la8: case RenderBase.OTextureFormat.hilo8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; output[outputOffset] = data[dataOffset]; output[outputOffset + 1] = data[dataOffset + 3]; outputOffset += 2; } } } break; case RenderBase.OTextureFormat.l8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; output[outputOffset] = data[dataOffset]; outputOffset++; } } } break; case RenderBase.OTextureFormat.a8: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; output[outputOffset] = data[dataOffset + 3]; outputOffset++; } } } break; case RenderBase.OTextureFormat.la4: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; output[outputOffset] = (byte)((data[dataOffset] / 16) << 4 | data[dataOffset + 3] / 16); outputOffset++; } } } break; case RenderBase.OTextureFormat.l4: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; byte c = (byte)(data[dataOffset++] >> 4); byte wholeByte = 0; if (toggle) { wholeByte = (byte)((wholeByte << 4) | (data[dataOffset++] >> 4)); output[outputOffset] = wholeByte; outputOffset++; } else { wholeByte = c; } toggle = !toggle; } } } break; case RenderBase.OTextureFormat.a4: for (int tY = 0; tY < img.Height / 8; tY++) { for (int tX = 0; tX < img.Width / 8; tX++) { for (int pixel = 0; pixel < 64; pixel++) { int x = tileOrder[pixel] % 8; int y = (tileOrder[pixel] - x) / 8; long dataOffset = ((tX * 8) + x + (((tY * 8 + y)) * img.Width)) * 4; byte c = (byte)(data[dataOffset + 3] >> 4); byte wholeByte = 0; if (toggle) { wholeByte = (byte)((wholeByte << 4) | (data[dataOffset++] >> 4)); output[outputOffset] = wholeByte; outputOffset++; } else { wholeByte = c; } toggle = !toggle; } } } break; case RenderBase.OTextureFormat.etc1: case RenderBase.OTextureFormat.etc1a4: int[] etc1Order = etc1Scramble(img.Width, img.Height); int i = 0; for (int tY = 0; tY < img.Height / 4; tY++) { for (int tX = 0; tX < img.Width / 4; tX++) { int TX = etc1Order[i] % (img.Width / 4); int TY = (etc1Order[i] - TX) / (img.Width / 4); outputOffset = (uint)((TX * 4) + (((TY * 4)) * (img.Width / 4))) * 2; if (format == RenderBase.OTextureFormat.etc1a4) { outputOffset = (uint)((TX * 4) + (((TY * 4)) * (img.Width / 4))) * 4; ulong alpha = 0; int iiii = 0; for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { long dataOffset = ((tX * 4) + (x) + (((tY * 4) + (y)) * img.Width)) * 4; uint a = (uint)data[dataOffset + 3] / 16; alpha |= (ulong)a << (iiii); iiii += 4; } } output[outputOffset + 7] = (byte)((alpha >> 56) & 0xFF); output[outputOffset + 6] = (byte)((alpha >> 48) & 0xFF); output[outputOffset + 5] = (byte)((alpha >> 40) & 0xFF); output[outputOffset + 4] = (byte)((alpha >> 32) & 0xFF); output[outputOffset + 3] = (byte)((alpha >> 24) & 0xFF); output[outputOffset + 2] = (byte)((alpha >> 16) & 0xFF); output[outputOffset + 1] = (byte)((alpha >> 8) & 0xFF); output[outputOffset] = (byte)(alpha & 0xFF); outputOffset += 8; } Color[] pixels = new Color[4 * 4]; for (int yy = 0; yy < 4; yy++) { for (int xx = 0; xx < 4; xx++) { long dataOffset = ((tX * 4) + (xx) + (((tY * 4) + (yy)) * img.Width)) * 4; //if (TX + xx >= img.Width) pixels[yy * 4 + xx] = Color.Transparent; //else if (TY + yy >= img.Height) pixels[yy * 4 + xx] = Color.Transparent; pixels[yy * 4 + xx] = Color.FromArgb((int)data[dataOffset + 0] | (int)data[dataOffset + 1] << 8 | (int)data[dataOffset + 2] << 16 | (int)data[dataOffset + 3] << 24); } } ulong ETC1Chunk = ETC1.GenETC1(pixels); output[outputOffset + 7] = (byte)((ETC1Chunk >> 56) & 0xFF); output[outputOffset + 6] = (byte)((ETC1Chunk >> 48) & 0xFF); output[outputOffset + 5] = (byte)((ETC1Chunk >> 40) & 0xFF); output[outputOffset + 4] = (byte)((ETC1Chunk >> 32) & 0xFF); output[outputOffset + 3] = (byte)((ETC1Chunk >> 24) & 0xFF); output[outputOffset + 2] = (byte)((ETC1Chunk >> 16) & 0xFF); output[outputOffset + 1] = (byte)((ETC1Chunk >> 8) & 0xFF); output[outputOffset + 0] = (byte)(ETC1Chunk & 0xFF); outputOffset += 8; i += 1; } } break; default: throw new NotImplementedException(); } return(output); }
/// <summary> /// Loads a Game freak texture. /// </summary> /// <param name="data">The texture data</param> /// <returns>The image as a texture</returns> public static Ohana3DS_Transfigured.Ohana.RenderBase.OTexture load(Stream data, bool keepOpen = false) { BinaryReader input = new BinaryReader(data); long descAddress = data.Position; data.Seek(8, SeekOrigin.Current); if (Ohana3DS_Transfigured.Ohana.IOUtils.readStringWithLength(input, 7) != "texture") { return(null); } data.Seek(descAddress + 0x18, SeekOrigin.Begin); int texLength = input.ReadInt32(); data.Seek(descAddress + 0x28, SeekOrigin.Begin); string texName = IOUtils.readStringWithLength(input, 0x40); data.Seek(descAddress + 0x68, SeekOrigin.Begin); ushort width = input.ReadUInt16(); ushort height = input.ReadUInt16(); ushort texFormat = input.ReadUInt16(); ushort texMipMaps = input.ReadUInt16(); data.Seek(0x10, SeekOrigin.Current); byte[] texBuffer = input.ReadBytes(texLength); RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare; switch (texFormat) { case 0x2: fmt = RenderBase.OTextureFormat.rgb565; break; case 0x3: fmt = RenderBase.OTextureFormat.rgb8; break; case 0x4: fmt = RenderBase.OTextureFormat.rgba8; break; case 0x16: fmt = RenderBase.OTextureFormat.rgba4; break; case 0x17: fmt = RenderBase.OTextureFormat.rgba5551; break; case 0x23: fmt = RenderBase.OTextureFormat.la8; break; case 0x24: fmt = RenderBase.OTextureFormat.hilo8; break; case 0x25: fmt = RenderBase.OTextureFormat.l8; break; case 0x26: fmt = RenderBase.OTextureFormat.a8; break; case 0x27: fmt = RenderBase.OTextureFormat.la4; break; case 0x28: fmt = RenderBase.OTextureFormat.l4; break; case 0x29: fmt = RenderBase.OTextureFormat.a4; break; case 0x2a: fmt = RenderBase.OTextureFormat.etc1; break; case 0x2b: fmt = RenderBase.OTextureFormat.etc1a4; break; default: Debug.WriteLine("Unk tex fmt " + texFormat.ToString("X4") + " @ " + texName); break; } Bitmap tex = TextureCodec.decode(texBuffer, width, height, fmt); if (!keepOpen) { data.Close(); } return(new RenderBase.OTexture(tex, texName)); }
private bool open(string fileName) { using (FileStream data = new FileStream(fileName, FileMode.Open)) { BinaryReader input = new BinaryReader(data); string magic = IOUtils.readString(input, 0); if (magic != "BCH") { return(false); } currentFile = fileName; data.Seek(4, SeekOrigin.Current); byte backwardCompatibility = input.ReadByte(); byte forwardCompatibility = input.ReadByte(); ushort version = input.ReadUInt16(); uint mainHeaderOffset = input.ReadUInt32(); uint stringTableOffset = input.ReadUInt32(); uint gpuCommandsOffset = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); uint dataExtendedOffset = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0; uint relocationTableOffset = input.ReadUInt32(); uint mainHeaderLength = input.ReadUInt32(); uint stringTableLength = input.ReadUInt32(); uint gpuCommandsLength = input.ReadUInt32(); uint dataLength = input.ReadUInt32(); uint dataExtendedLength = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0; uint relocationTableLength = input.ReadUInt32(); data.Seek(mainHeaderOffset, SeekOrigin.Begin); uint modelsPointerTableOffset = input.ReadUInt32() + mainHeaderOffset; uint modelsPointerTableEntries = input.ReadUInt32(); data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin); uint texturesPointerTableOffset = input.ReadUInt32() + mainHeaderOffset; uint texturesPointerTableEntries = input.ReadUInt32(); bch = new loadedBCH(); //Textures for (int index = 0; index < texturesPointerTableEntries; index++) { data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin); data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin); loadedTexture tex; tex.modified = false; tex.gpuCommandsOffset = input.ReadUInt32() + gpuCommandsOffset; tex.gpuCommandsWordCount = input.ReadUInt32(); data.Seek(0x14, SeekOrigin.Current); uint textureNameOffset = input.ReadUInt32(); string textureName = IOUtils.readString(input, textureNameOffset + stringTableOffset); data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin); PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount); tex.offset = textureCommands.getTexUnit0Address() + dataOffset; RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format(); Size textureSize = textureCommands.getTexUnit0Size(); switch (fmt) { case RenderBase.OTextureFormat.rgba8: tex.length = (textureSize.Width * textureSize.Height) * 4; break; case RenderBase.OTextureFormat.rgb8: tex.length = (textureSize.Width * textureSize.Height) * 3; break; case RenderBase.OTextureFormat.rgba5551: tex.length = (textureSize.Width * textureSize.Height) * 2; break; case RenderBase.OTextureFormat.rgb565: tex.length = (textureSize.Width * textureSize.Height) * 2; break; case RenderBase.OTextureFormat.rgba4: tex.length = (textureSize.Width * textureSize.Height) * 2; break; case RenderBase.OTextureFormat.la8: tex.length = (textureSize.Width * textureSize.Height) * 2; break; case RenderBase.OTextureFormat.hilo8: tex.length = (textureSize.Width * textureSize.Height) * 2; break; case RenderBase.OTextureFormat.l8: tex.length = textureSize.Width * textureSize.Height; break; case RenderBase.OTextureFormat.a8: tex.length = textureSize.Width * textureSize.Height; break; case RenderBase.OTextureFormat.la4: tex.length = textureSize.Width * textureSize.Height; break; case RenderBase.OTextureFormat.l4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break; case RenderBase.OTextureFormat.a4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break; case RenderBase.OTextureFormat.etc1: tex.length = (textureSize.Width * textureSize.Height) >> 1; break; case RenderBase.OTextureFormat.etc1a4: tex.length = textureSize.Width * textureSize.Height; break; default: throw new Exception("OBCHTextureReplacer: Invalid texture format on BCH!"); } while ((tex.length & 0x7f) > 0) { tex.length++; } data.Seek(tex.offset, SeekOrigin.Begin); byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4]; input.Read(buffer, 0, buffer.Length); Bitmap texture = TextureCodec.decode( buffer, textureSize.Width, textureSize.Height, fmt); tex.texture = new RenderBase.OTexture(texture, textureName); bch.textures.Add(tex); } //Materials for (int mdlIndex = 0; mdlIndex < modelsPointerTableEntries; mdlIndex++) { data.Seek(modelsPointerTableOffset + (mdlIndex * 4), SeekOrigin.Begin); data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin); data.Seek(0x34, SeekOrigin.Current); uint materialsTableOffset = input.ReadUInt32() + mainHeaderOffset; uint materialsTableEntries = input.ReadUInt32(); for (int index = 0; index < materialsTableEntries; index++) { if (backwardCompatibility < 0x21) { data.Seek(materialsTableOffset + (index * 0x58), SeekOrigin.Begin); } else { data.Seek(materialsTableOffset + (index * 0x2c), SeekOrigin.Begin); } loadedMaterial mat; data.Seek(0x10, SeekOrigin.Current); mat.gpuCommandsOffset = input.ReadUInt32() + gpuCommandsOffset; mat.gpuCommandsWordCount = input.ReadUInt32(); if (backwardCompatibility < 0x21) { data.Seek(0x30, SeekOrigin.Current); } else { data.Seek(4, SeekOrigin.Current); } uint texture0Offset = input.ReadUInt32() + stringTableOffset; uint texture1Offset = input.ReadUInt32() + stringTableOffset; uint texture2Offset = input.ReadUInt32() + stringTableOffset; mat.texture0 = IOUtils.readString(input, texture0Offset); mat.texture1 = IOUtils.readString(input, texture1Offset); mat.texture2 = IOUtils.readString(input, texture2Offset); bch.materials.Add(mat); } } bch.mainHeaderOffset = mainHeaderOffset; bch.gpuCommandsOffset = gpuCommandsOffset; bch.dataOffset = dataOffset; bch.relocationTableOffset = relocationTableOffset; bch.relocationTableLength = relocationTableLength; } updateTexturesList(); return(true); }