/// <summary> /// Method for reading I8 encoded data. /// </summary> /// <param name="br">BinaryReader to read with.</param> /// <param name="width">Texture width.</param> /// <param name="height">Texture height.</param> /// <returns>Texture as a stream containing RGBA data.</returns> private static byte[] ReadI8(DhBinaryReader br, ushort width, ushort height) { // Buffer to store our decoded data. byte[] decoded = new byte[width * height * 4]; // Loop through blocks y. for (int y = 0; y < (height / 4); y++) { // Loop through blocks y. for (int x = 0; x < (width / 8); x++) { // Loop through pixels in block y. for (int yx = 0; yx < 4; yx++) { // Loop through pixels in block y. for (int xy = 0; xy < 8; xy++) { // Determine if pixel is oob (x || y). if ((x * 8 + xy >= width) || (y * 4 + yx >= height)) { // Skip. continue; } // Read current pixel. byte data = br.Read(); // Calculate offset for decoded data. uint offset = (uint)(4 * (width * ((y * 4) + yx) + (x * 8) + xy)); // Store decoded data in buffer. decoded[offset + 0] = data; decoded[offset + 1] = data; decoded[offset + 2] = data; decoded[offset + 3] = 0xFF; } } } } // Return the texture as RGBA. return(decoded); }
private static byte[] ReadCMPR(DhBinaryReader br, uint width, uint height) { //Decode S3TC1 byte[] buffer = new byte[width * height * 4]; for (int y = 0; y < height / 4; y += 2) { for (int x = 0; x < width / 4; x += 2) { for (int yx = 0; yx < 2; ++yx) { for (int xy = 0; xy < 2; ++xy) { if (4 * (x + xy) < width && 4 * (y + yx) < height) { byte[] fileData = br.Read(8); //Array.Reverse(fileData); Buffer.BlockCopy(fileData, 0, buffer, (int)(8 * ((y + yx) * width / 4 + x + xy)), 8); } } } } } for (int i = 0; i < width * height / 2; i += 8) { // Micro swap routine needed Swap(ref buffer[i], ref buffer[i + 1]); Swap(ref buffer[i + 2], ref buffer[i + 3]); buffer[i + 4] = S3TC1ReverseByte(buffer[i + 4]); buffer[i + 5] = S3TC1ReverseByte(buffer[i + 5]); buffer[i + 6] = S3TC1ReverseByte(buffer[i + 6]); buffer[i + 7] = S3TC1ReverseByte(buffer[i + 7]); } //Now decompress the DXT1 data within it. return(DecompressDxt1(buffer, width, height)); }
/// <summary> /// Reads BIN from a byte array. /// </summary> /// <param name="data">The byte array containing the BIN data.</param> public BIN(byte[] data) { // Define a binary reader to read with. DhBinaryReader br = new DhBinaryReader(data, DhEndian.Big); // Read bin version. Version = br.Read(); // Make sure the bin version is either 0x01 or 0x02. if (Version == 0x00 || Version > 0x02) { throw new Exception($"[BIN] {Version} is not a valid version!"); } // Read bin model name. ModelName = br.ReadFixedStr(11); // Define a new list to hold the bin's offsets. Offsets = br.ReadU32s(21); // Go to the bin graph object offset. br.Goto(Offsets[12]); // Get first graph object, then all of it's attached graph objects. GraphObjects = GetGraphObjects(br, 0); // Make sure bin batches has a offset. if (Offsets[11] != 0) { // Go to the bin batches offset. br.Goto(Offsets[11]); // Define a new list to hold the bin's batches. Batches = new List <Batch>(); // Loop through batches. for (int i = 0; i < GetBatchCount(); i++) { // Read a batch and add it to the batch list. Batches.Add(new Batch(br, Offsets[11])); } } // Make sure bin shaders has a offset. if (Offsets[10] != 0) { // Go to the bin shaders offset. br.Goto(Offsets[10]); // Define a new list to hold the bin's shaders. Shaders = new List <Shader>(); // Loop through shaders. for (int i = 0; i < GetShaderCount(); i++) { // Read a shader and add it to the shader list. Shaders.Add(new Shader(br)); } } // Make sure bin texture coordinates 1 has a offset. if (Offsets[7] != 0) { // Go to the bin texture coordinates 1 offset. br.Goto(Offsets[7]); // Define a new list to hold the bin's texture coordinates 1. TextureCoordinates1 = new List <Vec2>(); // Loop through texture coordinates 1. for (int i = 0; i < GetTexCoordinate1Count(); i++) { // Read a bin texture coordinates and add it to the texture coordinates 1 list. TextureCoordinates1.Add(br.ReadVec2()); } } // Make sure bin texture coordinates 0 has a offset. if (Offsets[6] != 0) { // Go to the bin texture coordinates 0 offset. br.Goto(Offsets[6]); // Define a new list to hold the bin's texture coordinates 0. TextureCoordinates0 = new List <Vec2>(); // Loop through texture coordinates 0. for (int i = 0; i < GetTexCoordinate0Count(); i++) { // Read a bin texture coordinates 0 and add it to the texture coordinates 0 list. TextureCoordinates0.Add(br.ReadVec2()); } } // WE'RE SKIPPING COLOR0 AND COLOR1! (Offset5 and Offset4) // Make sure bin normals has a offset. if (Offsets[3] != 0) { // Go to the bin normals offset. br.Goto(Offsets[3]); // Define a new list to hold the bin's normals. Normals = new List <Vec3>(); // Loop through normals. for (int i = 0; i < GetNormalCount(); i++) { // Read a bin normal and add it to the normals list. Normals.Add(br.ReadVec3()); } } // Make sure bin positions has a offset. if (Offsets[2] != 0) { // Go to the bin positions offset. br.Goto(Offsets[2]); // Define a new list to hold the bin's positions. Positions = new List <Vec3>(); // Loop through positions. for (int i = 0; i < GetPositionCount(); i++) { // Read a bin position and add it to the positions list. Positions.Add(new Vec3(br.ReadF16() / 256.0f, br.ReadF16() / 256.0f, br.ReadF16() / 256.0f)); } } // Make sure bin materials has a offset. if (Offsets[1] != 0) { // Go to the bin materials offset. br.Goto(Offsets[1]); // Define a new list to hold the bin's materials. Materials = new List <Material>(); // Loop through materials. for (int i = 0; i < GetMaterialCount(); i++) { // Read a bin material and add it to the materials list. Materials.Add(new Material(br)); } } // Make sure bin textures has a offset. if (Offsets[0] != 0) { // Go to the bin textures offset. br.Goto(Offsets[0]); // Define a new list to hold the bin's textures. Textures = new List <BTI>(); // Loop through textures. for (int i = 0; i < GetTextureCount(); i++) { // Read a bin textures and add it to the textures list. Textures.Add(new BTI(br, Offsets[0])); } } }
/// <summary> /// Decompressing Yay0 compressed data. Full credits for this snippet goes to Daniel McCarthy: /// https://github.com/Daniel-McCarthy/Mr-Peeps-Compressor/blob/master/PeepsCompress/PeepsCompress/Algorithm%20Classes/YAY0.cs /// </summary> /// <param name="stream">Stream containing the Yay0 compressed data.</param> /// <returns>Decompressed data as a stream.</returns> public static Stream Decompress(Stream stream) { // Define a binary reader to read with. DhBinaryReader br = new DhBinaryReader(stream, DhEndian.Big); // Define a list of bytes to hold the decompressed data. List <byte> output = new List <byte>(); // Make sure the magic is "Yay0" if (br.ReadU32() != 1499560240) { throw new Exception("No valid Yay0 signature was found!"); } int decompressedLength = br.ReadS32(); int compressedOffset = br.ReadS32(); int decompressedOffset = br.ReadS32(); int layoutBitsOffset; // Begin decompression. while (output.Count < decompressedLength) { // Define variable to hold the layout byte. byte LayoutByte = br.Read(); // Define variable to hold the individual layout bits. BitArray LayoutBits = new BitArray(new byte[1] { LayoutByte }); // Loop through the layout bits. for (int i = 7; i > -1 && (output.Count < decompressedLength); i--) { // The next layout bit is 1. (Uncompressed data) if (LayoutBits[i] == true) { // Yay0 Non-compressed Data Chunk: // Add one byte from decompressedOffset to decompressedData. // Define variable to hold the current layout bits offset. layoutBitsOffset = (int)stream.Position; // Seek to the compressed data offset. stream.Seek(decompressedOffset, SeekOrigin.Begin); // Read a byte and add it to the list of output bytes. output.Add(br.Read()); // Advance the decompressed offset by one byte. decompressedOffset++; // Return to the current layout bits offset. stream.Seek(layoutBitsOffset, SeekOrigin.Begin); } // The next layout bit is 0. (Compressed data) else { // Yay0 Compressed Data Chunk: // Total length is 2 bytes. // 4 bits = Length // 12 bits = Offset // Define variable to hold the current offset. layoutBitsOffset = (int)stream.Position; // Seek to the compressed data offset. stream.Seek(compressedOffset, SeekOrigin.Begin); // Read the first byte of the compressed data. byte byte1 = br.Read(); // Read the second byte of the compressed data. byte byte2 = br.Read(); // Advance the compressed data offset by 2 (Since we just read 2 bytes) compressedOffset += 2; // Get 4 upper bits of the first byte. byte byte1Upper = (byte)((byte1 & 0x0F)); // Get 4 lower bits of the first byte. byte byte1Lower = (byte)((byte1 & 0xF0) >> 4); int finalOffset = ((byte1Upper << 8) | byte2) + 1; int finalLength; // Compressed chunk length is higher than 17. if (byte1Lower == 0) { stream.Seek(decompressedOffset, SeekOrigin.Begin); finalLength = br.Read() + 0x12; // Advance the decompressed offset by one byte. decompressedOffset++; } // Compressed chunk length is lower or equal to 17. else { // The data chunk length is equals to the first byte's 4 lower bits + 2. finalLength = byte1Lower + 2; } // Add data for finalLength iterations. for (int j = 0; j < finalLength; j++) { // Add byte at offset (fileSize - finalOffset) to file. output.Add(output[output.Count - finalOffset]); } // Return to the current layout bits offset. stream.Seek(layoutBitsOffset, SeekOrigin.Begin); } } } // Return decompressed data. return(new MemoryStream(output.ToArray())); }
/// <summary> /// Read a single batch from BIN. /// </summary> /// <param name="br">Binary Reader to use.</param> /// <param name="batchesOffset">Offset to batches.</param> public BinBatch(DhBinaryReader br, long batchesOffset) { // Read face count. FaceCount = br.ReadU16(); // Read primitive list size. ListSize = (ushort)(br.ReadS16() << 5); // Read vertex attributes. VertexAttributes = (BinBatchAttributes)br.ReadU32(); // Read UseNormals flag. UseNormals = br.Read(); // Read Position Winding. Positions = br.Read(); // Read UV Count. UvCount = br.Read(); // Read UseNBT flag. UseNBT = br.Read(); // Read Primitive offset. PrimitiveOffset = br.ReadU32(); // Define array to hold Unknown 1 values. Unknown1 = new int[2]; // Loop through Unknown 1 values. for (int i = 0; i < 2; i++) { // Read current Unknown 1 value. Unknown1[i] = br.ReadS32(); } // Save the current position. long currentPosition = br.Position(); // Go to the bin batches offset. br.Goto(batchesOffset); // Sail to the batches's primitive offset. br.Sail(PrimitiveOffset); // Define list to hold batch's primitives. Primitives = new List <BinPrimitive>(); // Define int to hold amount of faces read. int readFaces = 0; // Read primitives until batch's face count has been reached. while (readFaces < FaceCount && br.Position() < (batchesOffset + PrimitiveOffset + ListSize)) { // Read primitive. BinPrimitive binPrimitive = new BinPrimitive(br, UseNBT, UvCount, VertexAttributes); // Add the primitive to the batch's primitives. Primitives.Add(binPrimitive); // Add primitive's face count to the read faces counter. readFaces += binPrimitive.FaceCount; } // Go to the previously saved offset. br.Goto(currentPosition); }
/// <summary> /// Reads BIN from a stream. /// </summary> /// <param name="stream">The stream containing the BIN data.</param> public BIN(Stream stream) { // Define a binary reader to read with. DhBinaryReader br = new DhBinaryReader(stream, DhEndian.Big); // Read bin version. Version = br.Read(); // Make sure the bin version is either 0x01 or 0x02. if (Version == 0x00 || Version > 0x02) { throw new Exception(string.Format("{0} is not a valid bin version!", Version.ToString())); } // Read bin model name. ModelName = br.ReadStr(11).Trim('\0'); // Define a new list to hold the bin's offsets. Offsets = new List <uint>(); // Loop through the bin's offsets. for (int i = 0; i < 21; i++) { // Read offset and add it to the offsets list. Offsets.Add(br.ReadU32()); } // Go to the bin textures offset. br.Goto(Offsets[0]); // Define a new list to hold the bin's textures. Textures = new List <BinTexture>(); // Loop through bin's textures. TODO: This is static for now, add automatic texture count. for (int i = 0; i < 3; i++) { // Read texture and add it to the textures list. Textures.Add(new BinTexture(br, Offsets[0])); } // Go to the bin materials offset. br.Goto(Offsets[1]); // Define a new list to hold the bin's materials. Materials = new List <BinMaterial>(); // Loop through bin's materials. TODO: This is static for now, add automatic material count. for (int i = 0; i < 3; i++) { // Read texture and add it to the materials list. Materials.Add(new BinMaterial(br)); } // Go to the bin positions offset. br.Goto(Offsets[2]); // Define a new list to hold the bin's positions. Positions = new List <Vector3>(); // Loop through bin's positions. TODO: Fix this; This is a pretty shitty way to calculate amount of bin positions ... for (int i = 0; i < ((Math.Floor((decimal)(Offsets[3] - Offsets[2])) / 6) - 1); i++) { // Skip 6 bytes to "simulate" reading a bin position. br.Skip(6); // Make sure the currenet position has not passed the normals offset. if (!(br.Position() > Offsets[3])) { // Go back 6 bytes as we just "simulated" to read a bin position. br.Goto(br.Position() - 6); // Read a position and add it to the positions list. Positions.Add(new Vector3(br.ReadF16(), br.ReadF16(), br.ReadF16())); } } // Go to the bin normals offset. br.Goto(Offsets[3]); // Define a new list to hold the bin's normals. Normals = new List <Vector3>(); // Loop through bin's normals. TODO: This is static for now, add automatic normal count. for (int i = 0; i < 69; i++) { // Read a normal and add it to the normals list. Normals.Add(new Vector3(br.ReadF32(), br.ReadF32(), br.ReadF32())); } // Go to the bin texture coordinates offset. br.Goto(Offsets[6]); // Define a new list to hold the bin's texture coordinates. TextureCoordinates = new List <BinTextureCoordinate>(); // Loop through bin's texture coordinates. TODO: This is static for now, add automatic texture coordinates count. for (int i = 0; i < 72; i++) { // Read a bin texture coordinates and add it to the texture coordinates list. TextureCoordinates.Add(new BinTextureCoordinate(br)); } // Go to the bin shaders offset. br.Goto(Offsets[10]); // Define a new list to hold the bin's shaders. Shaders = new List <BinShader>(); // Loop through bin's shaders. TODO: This is static for now, add automatic shader count. for (int i = 0; i < 3; i++) { // Read a bin shader and add it to the shaders list. Shaders.Add(new BinShader(br)); } // Go to the bin batches offset. br.Goto(Offsets[11]); // Define a new list to hold the bin's batches. Batches = new List <BinBatch>(); // Loop through bin's batches. TODO: This is static for now, add automatic batch count. for (int i = 0; i < 2; i++) { // Read a bin batch and add it to the batches list. Batches.Add(new BinBatch(br, Offsets[11])); } }
/// <summary> /// Reads BTI from a stream. /// </summary> /// <param name="stream">The stream containing the BTI data.</param> public BTI(Stream stream) { // Define a binary reader to read with. DhBinaryReader br = new DhBinaryReader(stream, DhEndian.Big); // Read Texture Format. Format = (TextureFormat)br.Read(); // Read Alpha Flag. AlphaFlag = br.Read(); // Read Width. Width = br.ReadU16(); // Read Height. Height = br.ReadU16(); // Read WrapS. WrapS = (WrapMode)br.Read(); // Read WrapT. WrapT = (WrapMode)br.Read(); // Read Palette Format. PaletteFormat = (PaletteFormat)br.ReadU16(); // Read Palette Count. PaletteCount = br.ReadU16(); // Read Palette Offset. PaletteOffset = br.ReadU32(); // Read Unknown 1. Unknown1 = br.ReadU32(); // Read MinFilterMode. MinFilterMode = (FilterMode)br.Read(); // Read MagFilterMode. MagFilterMode = (FilterMode)br.Read(); // Read MinLOD. MinLOD = br.ReadU16(); // Read MagLOD. MagLOD = br.Read(); // Read MipMap Count. MipMapCount = br.Read(); // Read LodBias. LodBias = br.ReadU16(); // Read DataOffset. DataOffset = br.ReadU32(); // Read data. Data = br.Read((Width * Height) / 2); // Read Data. //Data = br.ReadAt(DataOffset, (Width * Height) / 4); }
/// <summary> /// Reads BTI using a BinaryReader. (BinTexture Format) /// </summary> /// <param name="br">The BinaryReader to use.</param> public BTI(DhBinaryReader br, uint textureOffset, uint textureLocationOffset) { // Set Texture Format. byte format = br.Read(); // Texture Formats IDs are different in MDL's for some reason. switch (format) { case 3: Format = TextureFormat.I4; break; case 4: Format = TextureFormat.I8; break; case 6: Format = TextureFormat.IA8; break; case 7: Format = TextureFormat.RGB565; break; case 8: Format = TextureFormat.RGB5A3; break; case 10: Format = TextureFormat.CMPR; break; default: throw new Exception($"Texture format {format} is not supported yet!"); } // Set Alpha Flag. AlphaFlag = br.Read(); // Set Width. Width = br.ReadU16(); // Set Height. Height = br.ReadU16(); // Skip 26 bytes of padding. br.Skip(26); // Read data. int length = 0; switch (Format) { case TextureFormat.C4: length = Width * Height * 8; break; case TextureFormat.C8: length = Width * Height * 8; break; default: length = Width * Height * 4; break; } Data = br.Read(length); }
/// <summary> /// Reads BTI using a BinaryReader. (BinTexture Format) /// </summary> /// <param name="br">The BinaryReader to use.</param> public BTI(DhBinaryReader br, uint textureHeader) { // Set Width. Width = br.ReadU16(); // Set Height. Height = br.ReadU16(); // Set Texture Format. Format = (TextureFormat)br.Read(); // Set Alpha Flag. AlphaFlag = br.Read(); // Set WrapS. WrapS = WrapMode.ClampToEdge; // Set WrapT. WrapT = WrapMode.ClampToEdge; // Set Palette Format. PaletteFormat = PaletteFormat.RGB565; // Set Palette Count. PaletteCount = 0; // Set Palette Offset. PaletteOffset = 0; // Set Unknown 1. (Padding?) Unknown1 = 0; // Set MinFilterMode. MinFilterMode = FilterMode.Linear; // Set MagFilterMode. MagFilterMode = FilterMode.Linear; // Set MinLOD. MinLOD = 0; // Set MagLOD. MagLOD = 1; // Set MipMap Count. MipMapCount = 0; // Set LodBias. LodBias = 0; // Skip 2 bytes of padding. br.Skip(2); // Set Data Offset. DataOffset = br.ReadU32(); // Read data. int length = 0; switch (Format) { case TextureFormat.C4: length = Width * Height * 8; break; case TextureFormat.C8: length = Width * Height * 8; break; default: length = Width * Height * 4; break; } Data = br.ReadAt((textureHeader) + DataOffset, length); }
/// <summary> /// Reads MP from a byte array. /// </summary> /// <param name="data">The byte array containing the MP data.</param> public MP(byte[] data) { // Define a binary reader to read with. DhBinaryReader br = new DhBinaryReader(data, DhEndian.Big); // Read Grid Scale. GridScale = br.ReadVec3(); // Read Minimum Bounds. MinimumBounds = br.ReadVec3(); // Read Axis Lengths. AxisLengths = br.ReadVec3(); // Define new array to hold offsets. Offsets = new int[7]; // Loop through our offsets. for (int i = 0; i < 7; i++) { // Read offset and store it to the offsets array. Offsets[i] = br.ReadS32(); } // Go to mp's vertex data offset. br.Goto(Offsets[0]); // Calculate amount of vertices. int verticeCount = (Offsets[1] - Offsets[0]) / 12; // Define new list to hold vertices. Vertices = new List <Vec3>(); // Loop through vertices. for (int i = 0; i < verticeCount; i++) { // Read vertex and add it to the list of vertices. Vertices.Add(br.ReadVec3()); } // Go to mp's normals offset. br.Goto(Offsets[1]); // Calculate amount of normals. int normalCount = (Offsets[2] - Offsets[1]) / 12; // Define new list to hold normals. Normals = new List <Vec3>(); // Loop through normals. for (int i = 0; i < normalCount; i++) { // Read normal and add it to the normal list. Normals.Add(br.ReadVec3()); } // Go to mp's triangle data offset. br.Goto(Offsets[2]); // Calculate amount of triangles. int triangleDataCount = (Offsets[3] - Offsets[2]) / 24; // Define new list to hold triangle data. TriangleData = new List <TriangleData>(); // Loop through triangles. for (int i = 0; i < triangleDataCount; i++) { // Read triangle and add it to the triangle data list. TriangleData.Add(new TriangleData(br)); } // Go to mp's triangle group offset. br.Goto(Offsets[3]); // Define new list to hold triangle groups. TriangleGroups = new List <TriangleGroup>(); // Make sure first ushort is 0xFFFF. if (br.ReadU16() != 0xFFFF) { throw new FormatException("Start of triangle groups section was not 0xFFFF!"); } // We'll read triangle groups as long as we're not entering the next section. while (br.Position() < Offsets[4] - 2) { // Read group and add it to the groups list. TriangleGroups.Add(new TriangleGroup(br)); } // Make sure last ushort is 0xFFFF. if (br.ReadU16() != 0xFFFF) { throw new FormatException("End of triangle groups section was not 0xFFFF!"); } // Go to mp's grid index offset. br.Goto(Offsets[4]); // Calculate amount of grid index entries. (Offset 5 is a duplicate of offset 4) int gridIndexCount = (Offsets[6] - Offsets[4]) / 8; // Define new list to hold grid indices. GridIndices = new List <GridIndex>(); // Loop through grid indices. for (int i = 0; i < gridIndexCount; i++) { GridIndices.Add(new GridIndex(br)); } // Go to mp's unknown offset. br.Goto(Offsets[6]); // Define new list to hold unknown data. Unknowns = new List <Unknown>(); // We'll read unknowns as long as we're not reading padding EOF. while (br.Position() < br.GetStream().Length) { // Check if next byte is 0xFF. (More data) if (br.Read() == 0xFF) { // Jump back a byte, since we checked for data. br.Sail(-1); // Read unknown and add it to the unknowns list. Unknowns.Add(new Unknown(br)); } else { // We've reached padding, stop reading. break; } } }
/// <summary> /// Read a single unknown from MP. /// </summary> /// <param name="br">The BinaryReader to read with.</param> public Unknown(DhBinaryReader br) { // Read unknown 1. (3 bytes) Unknown1 = br.Read(3); }