public TEX(Stream input) { using (var br = new BinaryReaderX(input)) { // Set endianess if (br.PeekString() == "\0XET") { br.ByteOrder = ByteOrder = ByteOrder.BigEndian; } // Header Header = br.ReadType <FileHeader>(); HeaderInfo = new FileHeaderInfo { // Block 1 Version = (Version)(Header.Block1 & 0xFFF), Unknown1 = (int)((Header.Block1 >> 12) & 0xFFF), Unused1 = (int)((Header.Block1 >> 24) & 0xF), AlphaChannelFlags = (AlphaChannelFlags)((Header.Block1 >> 28) & 0xF), // Block 2 MipMapCount = (int)(Header.Block2 & 0x3F), Width = (int)((Header.Block2 >> 6) & 0x1FFF), Height = Math.Max((int)((Header.Block2 >> 19) & 0x1FFF), MinHeight), // Block 3 Unknown2 = (int)(Header.Block3 & 0xFF), Format = (byte)((Header.Block3 >> 8) & 0xFF), Unknown3 = (int)((Header.Block3 >> 16) & 0xFFFF) }; if (HeaderInfo.Version == Version._Switchv1 && !SwitchFormats.ContainsKey(HeaderInfo.Format)) { throw new ImageFormatException($"Switch texture format 0x{HeaderInfo.Format.ToString("X2")} is not implemented."); } else if (!Formats.ContainsKey(HeaderInfo.Format)) { throw new ImageFormatException($"Texture format 0x{HeaderInfo.Format.ToString("X2")} is not implemented."); } // TODO: Consider whether the following settings make more sense if conditioned by the ByteOrder (or Platform) //var format = HeaderInfo.Format.ToString().StartsWith("DXT1") ? Format.DXT1 : HeaderInfo.Format.ToString().StartsWith("DXT5") ? Format.DXT5 : HeaderInfo.Format; Settings.Format = (HeaderInfo.Version == Version._Switchv1) ? SwitchFormats[HeaderInfo.Format] : Formats[HeaderInfo.Format]; List <int> mipMaps = null; if (HeaderInfo.Version == Version._Switchv1) { var texOverallSize = br.ReadInt32(); if (texOverallSize > br.BaseStream.Length) { br.BaseStream.Position -= 4; SwitchUnknownData = br.ReadBytes(0x6C); texOverallSize = br.ReadInt32(); HeaderInfo.MipMapCount++; } mipMaps = br.ReadMultiple <int>(HeaderInfo.MipMapCount); } else if (HeaderInfo.Version != Version._3DSv1) { mipMaps = br.ReadMultiple <int>(HeaderInfo.MipMapCount); } for (var i = 0; i < mipMaps.Count; i++) { var texDataSize = 0; if (SwitchUnknownData != null) { if (i + 1 == HeaderInfo.MipMapCount) { continue; } texDataSize = mipMaps[i + 1] - mipMaps[i]; } else if (HeaderInfo.Version != Version._3DSv1) { texDataSize = (i + 1 < HeaderInfo.MipMapCount ? mipMaps[i + 1] : (int)br.BaseStream.Length) - mipMaps[i]; } else { texDataSize = Formats[HeaderInfo.Format].BitDepth * (HeaderInfo.Width >> i) * (HeaderInfo.Height >> i) / 8; } Settings.Width = Math.Max(HeaderInfo.Width >> i, 2); Settings.Height = Math.Max(HeaderInfo.Height >> i, 2); //Set possible Swizzles if (HeaderInfo.Version == Version._3DSv1 || HeaderInfo.Version == Version._3DSv2 || HeaderInfo.Version == Version._3DSv3) { Settings.Swizzle = new CTRSwizzle(Settings.Width, Settings.Height); } else if (HeaderInfo.Version == Version._Switchv1) { Settings.Swizzle = new NXSwizzle(Settings.Width, Settings.Height, Settings.Format.BitDepth, GetSwitchSwizzleFormat(Settings.Format.FormatName)); } else if (Settings.Format.FormatName.Contains("DXT")) { Settings.Swizzle = new BlockSwizzle(Settings.Width, Settings.Height); } //Set possible pixel shaders if ((Format)HeaderInfo.Format == Format.DXT5_B) { Settings.PixelShader = ToNoAlpha; } else if ((Format)HeaderInfo.Format == Format.DXT5_YCbCr) { Settings.PixelShader = ToProperColors; } Bitmaps.Add(Common.Load(br.ReadBytes(texDataSize), Settings)); } if (SwitchUnknownData != null) { SwitchOverflowingData = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position)); } } }