/// <summary> /// Loads a Dragon Quest VII image. /// </summary> /// <param name="data">The DMP image data</param> /// <returns>The image as a texture</returns> public static RenderBase.OTexture load(Stream data) { BinaryReader input = new BinaryReader(data); string dmpMagic = IOUtils.readString(input, 0, 3); if (dmpMagic != "DMP") { throw new Exception("DMP: Invalid or corrupted file!"); } string format = IOUtils.readString(input, 4, 4); int width = input.ReadUInt16(); int height = input.ReadUInt16(); int pow2Width = input.ReadUInt16(); int pow2Height = input.ReadUInt16(); byte[] buffer = new byte[data.Length - 0x10]; data.Read(buffer, 0, buffer.Length); data.Close(); Bitmap bmp = null; switch (format) { case "8888": bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba8); break; } Bitmap newBmp = new Bitmap(width, height); Graphics gfx = Graphics.FromImage(newBmp); gfx.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel); gfx.Dispose(); return(new RenderBase.OTexture(newBmp, "texture")); }
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> /// Loads a Fantasy Life ZTEX texture from a Stream. /// </summary> /// <param name="data">The Stream with the data</param> /// <returns>The list of textures</returns> public static List <RenderBase.OTexture> load(Stream data) { List <RenderBase.OTexture> textures = new List <RenderBase.OTexture>(); BinaryReader input = new BinaryReader(data); string ztexMagic = IOUtils.readString(input, 0, 4); ushort textureCount = input.ReadUInt16(); input.ReadUInt16(); input.ReadUInt32(); List <textureEntry> entries = new List <textureEntry>(); for (int i = 0; i < textureCount; i++) { textureEntry entry = new textureEntry(); entry.name = IOUtils.readString(input, (uint)(0xc + (i * 0x58))); data.Seek(0xc + (i * 0x58) + 0x40, SeekOrigin.Begin); input.ReadUInt32(); entry.offset = input.ReadUInt32(); input.ReadUInt32(); entry.length = input.ReadUInt32(); entry.width = input.ReadUInt16(); entry.height = input.ReadUInt16(); input.ReadByte(); entry.format = input.ReadByte(); input.ReadUInt16(); entries.Add(entry); } foreach (textureEntry entry in entries) { data.Seek(entry.offset, SeekOrigin.Begin); byte[] buffer = new byte[entry.length]; data.Read(buffer, 0, buffer.Length); Bitmap bmp = null; switch (entry.format) { case 1: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgb565); break; case 5: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgba4); break; case 9: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgba8); break; case 0x18: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.etc1); break; case 0x19: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.etc1a4); break; } textures.Add(new RenderBase.OTexture(bmp, entry.name)); } data.Close(); return(textures); }
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> /// Loads a Binary Citra Layout Image from a Stream. /// </summary> /// <param name="data">The Stream with the data</param> /// <returns>The image as a texture</returns> public static RenderBase.OTexture load(Stream data) { BinaryReader input = new BinaryReader(data); data.Seek(-0x28, SeekOrigin.End); //Note: Stella Glow uses Little Endian BFLIMs, so please don't check the magic ;) string climMagic = IOUtils.readStringWithLength(input, 4); ushort endian = input.ReadUInt16(); uint climHeaderLength = input.ReadUInt32(); input.ReadUInt16(); uint fileLength = input.ReadUInt32(); input.ReadUInt32(); string imagMagic = IOUtils.readStringWithLength(input, 4); uint imagHeaderLength = input.ReadUInt32(); ushort width = input.ReadUInt16(); ushort height = input.ReadUInt16(); uint format = input.ReadUInt32(); uint length = input.ReadUInt32(); if (climMagic == "FLIM") { format = (format >> 16) & 0xf; } data.Seek(-(length + 0x28), SeekOrigin.End); byte[] buffer = new byte[length]; data.Read(buffer, 0, buffer.Length); data.Close(); int pow2Width = (int)(Math.Pow(2, Math.Ceiling(Math.Log(width) / Math.Log(2)))); int pow2Height = (int)(Math.Pow(2, Math.Ceiling(Math.Log(height) / Math.Log(2)))); Bitmap bmp = null; switch (format) { case 0: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.l8); break; case 1: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.a8); break; case 2: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.la4); break; case 3: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.la8); break; case 4: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.hilo8); break; case 5: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgb565); break; case 6: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgb8); break; case 7: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba5551); break; case 8: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba4); break; case 9: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba8); break; case 0xa: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.etc1); break; case 0xb: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.etc1a4); break; case 0xc: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.l4); break; } Bitmap newBmp = new Bitmap(width, height); Graphics gfx = Graphics.FromImage(newBmp); gfx.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel); gfx.Dispose(); return(new RenderBase.OTexture(newBmp, "texture")); }
/// <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); }