diff --git a/BadConnectionException.cs b/BadConnectionException.cs new file mode 100644 index 0000000..d6c930e --- /dev/null +++ b/BadConnectionException.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace UnifiedFlashingPlatform +{ + public class BadConnectionException : Exception { public BadConnectionException() { } public BadConnectionException(string message) : base(message) { } public BadConnectionException(string message, Exception innerException) : base(message, innerException) { } } +} \ No newline at end of file diff --git a/BigEndian.cs b/BigEndian.cs new file mode 100644 index 0000000..b566c04 --- /dev/null +++ b/BigEndian.cs @@ -0,0 +1,122 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace UnifiedFlashingPlatform +{ + internal static class BigEndian + { + public static byte[] GetBytes(object Value) + { + byte[] Bytes; + if (Value is short) + { + Bytes = BitConverter.GetBytes((short)Value); + } + else if (Value is ushort) + { + Bytes = BitConverter.GetBytes((ushort)Value); + } + else if (Value is int) + { + Bytes = BitConverter.GetBytes((int)Value); + } + else + { + Bytes = Value is uint ? BitConverter.GetBytes((uint)Value) : throw new NotSupportedException(); + } + + byte[] Result = new byte[Bytes.Length]; + for (int i = 0; i < Bytes.Length; i++) + { + Result[i] = Bytes[Bytes.Length - 1 - i]; + } + + return Result; + } + + public static byte[] GetBytes(object Value, int Width) + { + byte[] Result; + byte[] BigEndianBytes = GetBytes(Value); + if (BigEndianBytes.Length == Width) + { + return BigEndianBytes; + } + else if (BigEndianBytes.Length > Width) + { + Result = new byte[Width]; + Buffer.BlockCopy(BigEndianBytes, BigEndianBytes.Length - Width, Result, 0, Width); + return Result; + } + else + { + Result = new byte[Width]; + Buffer.BlockCopy(BigEndianBytes, 0, Result, Width - BigEndianBytes.Length, BigEndianBytes.Length); + return Result; + } + } + + public static UInt16 ToUInt16(byte[] Buffer, int Offset) + { + byte[] Bytes = new byte[2]; + for (int i = 0; i < 2; i++) + { + Bytes[i] = Buffer[Offset + 1 - i]; + } + + return BitConverter.ToUInt16(Bytes, 0); + } + + public static Int16 ToInt16(byte[] Buffer, int Offset) + { + byte[] Bytes = new byte[2]; + for (int i = 0; i < 2; i++) + { + Bytes[i] = Buffer[Offset + 1 - i]; + } + + return BitConverter.ToInt16(Bytes, 0); + } + + public static UInt32 ToUInt32(byte[] Buffer, int Offset) + { + byte[] Bytes = new byte[4]; + for (int i = 0; i < 4; i++) + { + Bytes[i] = Buffer[Offset + 3 - i]; + } + + return BitConverter.ToUInt32(Bytes, 0); + } + + public static Int32 ToInt32(byte[] Buffer, int Offset) + { + byte[] Bytes = new byte[4]; + for (int i = 0; i < 4; i++) + { + Bytes[i] = Buffer[Offset + 3 - i]; + } + + return BitConverter.ToInt32(Bytes, 0); + } + } +} \ No newline at end of file diff --git a/ByteOperations.cs b/ByteOperations.cs new file mode 100644 index 0000000..4fa6ee8 --- /dev/null +++ b/ByteOperations.cs @@ -0,0 +1,401 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// These functions assume same endianness for the CPU architecture and the raw data it reads from or writes to. + +using System; +using System.IO; + +namespace UnifiedFlashingPlatform +{ + internal static class ByteOperations + { + internal static string ReadAsciiString(byte[] ByteArray, UInt32 Offset, UInt32 Length) + { + byte[] Bytes = new byte[Length]; + Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length); + return System.Text.Encoding.ASCII.GetString(Bytes); + } + + internal static string ReadUnicodeString(byte[] ByteArray, UInt32 Offset, UInt32 Length) + { + byte[] Bytes = new byte[Length]; + Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length); + return System.Text.Encoding.Unicode.GetString(Bytes); + } + + internal static void WriteAsciiString(byte[] ByteArray, UInt32 Offset, string Text, UInt32? MaxBufferLength = null) + { + if (MaxBufferLength != null) + { + Array.Clear(ByteArray, (int)Offset, (int)MaxBufferLength); + } + + byte[] TextBytes = System.Text.Encoding.ASCII.GetBytes(Text); + int WriteLength = TextBytes.Length; + if (WriteLength > MaxBufferLength) + { + WriteLength = (int)MaxBufferLength; + } + + Buffer.BlockCopy(TextBytes, 0, ByteArray, (int)Offset, WriteLength); + } + + internal static void WriteUnicodeString(byte[] ByteArray, UInt32 Offset, string Text, UInt32? MaxBufferLength = null) + { + if (MaxBufferLength != null) + { + Array.Clear(ByteArray, (int)Offset, (int)MaxBufferLength); + } + + byte[] TextBytes = System.Text.Encoding.Unicode.GetBytes(Text); + int WriteLength = TextBytes.Length; + if (WriteLength > MaxBufferLength) + { + WriteLength = (int)MaxBufferLength; + } + + Buffer.BlockCopy(TextBytes, 0, ByteArray, (int)Offset, WriteLength); + } + + internal static UInt32 ReadUInt32(byte[] ByteArray, UInt32 Offset) + { + return BitConverter.ToUInt32(ByteArray, (int)Offset); + } + + internal static void WriteUInt32(byte[] ByteArray, UInt32 Offset, UInt32 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4); + } + + internal static Int32 ReadInt32(byte[] ByteArray, UInt32 Offset) + { + return BitConverter.ToInt32(ByteArray, (int)Offset); + } + + internal static void WriteInt32(byte[] ByteArray, UInt32 Offset, Int32 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4); + } + + internal static UInt16 ReadUInt16(byte[] ByteArray, UInt32 Offset) + { + return BitConverter.ToUInt16(ByteArray, (int)Offset); + } + + internal static void WriteUInt16(byte[] ByteArray, UInt32 Offset, UInt16 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2); + } + + internal static Int16 ReadInt16(byte[] ByteArray, UInt32 Offset) + { + return BitConverter.ToInt16(ByteArray, (int)Offset); + } + + internal static void WriteInt16(byte[] ByteArray, UInt32 Offset, Int16 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2); + } + + internal static byte ReadUInt8(byte[] ByteArray, UInt32 Offset) + { + return ByteArray[Offset]; + } + + internal static void WriteUInt8(byte[] ByteArray, UInt32 Offset, byte Value) + { + ByteArray[Offset] = Value; + } + + internal static UInt32 ReadUInt24(byte[] ByteArray, UInt32 Offset) + { + return (UInt32)(ByteArray[Offset] + (ByteArray[Offset + 1] << 8) + (ByteArray[Offset + 2] << 16)); + } + + internal static void WriteUInt24(byte[] ByteArray, UInt32 Offset, UInt32 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 3); + } + + internal static UInt64 ReadUInt64(byte[] ByteArray, UInt32 Offset) + { + return BitConverter.ToUInt64(ByteArray, (int)Offset); + } + + internal static void WriteUInt64(byte[] ByteArray, UInt32 Offset, UInt64 Value) + { + Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 8); + } + + internal static Guid ReadGuid(byte[] ByteArray, UInt32 Offset) + { + byte[] GuidBuffer = new byte[0x10]; + Buffer.BlockCopy(ByteArray, (int)Offset, GuidBuffer, 0, 0x10); + return new Guid(GuidBuffer); + } + + internal static void WriteGuid(byte[] ByteArray, UInt32 Offset, Guid Value) + { + Buffer.BlockCopy(Value.ToByteArray(), 0, ByteArray, (int)Offset, 0x10); + } + + internal static UInt32 Align(UInt32 Base, UInt32 Offset, UInt32 Alignment) + { + if (((Offset - Base) % Alignment) == 0) + { + return Offset; + } + else + { + return ((((Offset - Base) / Alignment) + 1) * Alignment) + Base; + } + } + + internal static UInt32? FindPatternInFile(string FileName, byte[] Pattern, byte[] Mask, out byte[] OutPattern) + { + // The mask is optional. + // In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard. + + UInt32? Result = null; + + FileStream Stream = new(FileName, FileMode.Open, FileAccess.Read); + + byte[] Buffer = new byte[0x10000 + Pattern.Length - 1]; + UInt32 BufferReadPosition = 0; // Position in buffer where file-chunk is being read. + UInt32 BytesInBuffer = 0; + UInt32 BytesRead; + UInt32 SearchPositionFile = 0; + UInt32 SearchPositionBuffer = 0; + UInt32 BufferFileOffset = 0; // Offset in file where data from buffer is located. + bool Match = false; + int i; + + OutPattern = null; + + while (SearchPositionFile <= (Stream.Length - Pattern.Length)) + { + if ((SearchPositionBuffer + Pattern.Length) > BytesInBuffer) + { + // Need to read next chunk + if ((BytesInBuffer - SearchPositionBuffer) > 0) + { + System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, Buffer, 0, (int)(BytesInBuffer - SearchPositionBuffer)); + } + BufferReadPosition = BytesInBuffer - SearchPositionBuffer; + BytesInBuffer -= SearchPositionBuffer; + BufferFileOffset += SearchPositionBuffer; + SearchPositionBuffer = 0; + + BytesRead = (UInt32)Stream.Read(Buffer, (int)BufferReadPosition, Buffer.Length - (int)BufferReadPosition); + BytesInBuffer += BytesRead; + } + + Match = true; + for (i = 0; i < Pattern.Length; i++) + { + if (Buffer[SearchPositionBuffer + i] != Pattern[i]) + { + if ((Mask == null) || (Mask[i] == 0)) + { + Match = false; + break; + } + } + } + + if (Match) + { + Result = SearchPositionFile; + + OutPattern = new byte[Pattern.Length]; + System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, OutPattern, 0, Pattern.Length); + break; + } + + SearchPositionBuffer++; + SearchPositionFile++; + } + + Stream.Close(); + + return Result; + } + + internal static UInt32? FindAscii(byte[] SourceBuffer, string Pattern) + { + return FindPattern(SourceBuffer, System.Text.Encoding.ASCII.GetBytes(Pattern), null, null); + } + + internal static UInt32? FindUnicode(byte[] SourceBuffer, string Pattern) + { + return FindPattern(SourceBuffer, System.Text.Encoding.Unicode.GetBytes(Pattern), null, null); + } + + internal static UInt32? FindUint(byte[] SourceBuffer, UInt32 Pattern) + { + return FindPattern(SourceBuffer, BitConverter.GetBytes(Pattern), null, null); + } + + internal static UInt32? FindPattern(byte[] SourceBuffer, byte[] Pattern, byte[] Mask, byte[] OutPattern) + { + return FindPattern(SourceBuffer, 0, null, Pattern, Mask, OutPattern); + } + + internal static bool Compare(byte[] Array1, byte[] Array2) + { + return System.Collections.StructuralComparisons.StructuralEqualityComparer.Equals(Array1, Array2); + } + + internal static UInt32? FindPattern(byte[] SourceBuffer, uint SourceOffset, uint? SourceSize, byte[] Pattern, byte[] Mask, byte[] OutPattern) + { + // The mask is optional. + // In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard. + + UInt32? Result = null; + + UInt32 SearchPosition = SourceOffset; + bool Match = false; + int i; + + while ((SearchPosition <= (SourceBuffer.Length - Pattern.Length)) && ((SourceSize == null) || (SearchPosition <= (SourceOffset + SourceSize - Pattern.Length)))) + { + Match = true; + for (i = 0; i < Pattern.Length; i++) + { + if (SourceBuffer[SearchPosition + i] != Pattern[i]) + { + if ((Mask == null) || (Mask[i] == 0)) + { + Match = false; + break; + } + } + } + + if (Match) + { + Result = SearchPosition; + + if (OutPattern != null) + { + Buffer.BlockCopy(SourceBuffer, (int)SearchPosition, OutPattern, 0, Pattern.Length); + } + + break; + } + + SearchPosition++; + } + + return Result; + } + + internal static byte CalculateChecksum8(byte[] Buffer, UInt32 Offset, UInt32 Size) + { + byte Checksum = 0; + + for (UInt32 i = Offset; i < (Offset + Size); i++) + { + Checksum += Buffer[i]; + } + + return (byte)(0x100 - Checksum); + } + + internal static UInt16 CalculateChecksum16(byte[] Buffer, UInt32 Offset, UInt32 Size) + { + UInt16 Checksum = 0; + + for (UInt32 i = Offset; i < (Offset + Size - 1); i += 2) + { + Checksum += BitConverter.ToUInt16(Buffer, (int)i); + } + + return (UInt16)(0x10000 - Checksum); + } + + private static readonly UInt32[] CRC32Table = new UInt32[] { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + internal static UInt32 CRC32(byte[] Input, UInt32 Offset, UInt32 Length) + { + if ((Input == null) || ((Offset + Length) > Input.Length)) + { + throw new ArgumentException(); + } + + unchecked + { + uint crc = (uint)(((uint)0) ^ (-1)); + for (var i = Offset; i < (Offset + Length); i++) + { + crc = (crc >> 8) ^ CRC32Table[(crc ^ Input[i]) & 0xFF]; + } + crc = (uint)(crc ^ (-1)); + + return crc; + } + } + } +} \ No newline at end of file diff --git a/DecompressedStream.cs b/DecompressedStream.cs new file mode 100644 index 0000000..4b17ac4 --- /dev/null +++ b/DecompressedStream.cs @@ -0,0 +1,133 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace UnifiedFlashingPlatform +{ + // For reading a compressed stream or normal stream + internal class DecompressedStream : Stream + { + private readonly Stream UnderlyingStream; + private readonly bool IsSourceCompressed; + private readonly UInt64 DecompressedLength; + private Int64 ReadPosition = 0; + + // For reading a compressed stream + internal DecompressedStream(Stream InputStream) + { + UnderlyingStream = new ReadSeekableStream(InputStream, 0x100); + + byte[] Signature = new byte["CompressedPartition".Length + 2]; + Signature[0x00] = 0xFF; + Buffer.BlockCopy(Encoding.ASCII.GetBytes("CompressedPartition"), 0, Signature, 0x01, "CompressedPartition".Length); + Signature["CompressedPartition".Length + 1] = 0x00; + + int PrimaryHeaderSize = 0x0A + "CompressedPartition".Length; + byte[] SignatureRead = new byte[Signature.Length]; + UnderlyingStream.Read(SignatureRead, 0, Signature.Length); + + IsSourceCompressed = StructuralComparisons.StructuralEqualityComparer.Equals(Signature, SignatureRead); + if (IsSourceCompressed) + { + byte[] FormatVersionBytes = new byte[4]; + UnderlyingStream.Read(FormatVersionBytes, 0, 4); + if (BitConverter.ToUInt32(FormatVersionBytes, 0) > 1) // Max supported format version = 1 + { + throw new InvalidDataException(); + } + + byte[] HeaderSizeBytes = new byte[4]; + UnderlyingStream.Read(HeaderSizeBytes, 0, 4); + UInt32 HeaderSize = BitConverter.ToUInt32(HeaderSizeBytes, 0); + + if (HeaderSize >= (Signature.Length + 0x10)) + { + byte[] DecompressedLengthBytes = new byte[8]; + UnderlyingStream.Read(DecompressedLengthBytes, 0, 8); + DecompressedLength = BitConverter.ToUInt64(DecompressedLengthBytes, 0); + } + else + { + throw new InvalidDataException(); + } + + UInt32 HeaderBytesRemaining = (UInt32)(HeaderSize - Signature.Length - 0x10); + if (HeaderBytesRemaining > 0) + { + byte[] HeaderBytes = new byte[HeaderBytesRemaining]; + UnderlyingStream.Read(HeaderBytes, 0, (int)HeaderBytesRemaining); + } + + UnderlyingStream = new GZipStream(UnderlyingStream, CompressionMode.Decompress, false); + } + else + { + UnderlyingStream.Position = 0; + } + } + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override int Read(byte[] buffer, int offset, int count) + { + int RealCount = UnderlyingStream.Read(buffer, offset, count); + ReadPosition += RealCount; + return RealCount; + } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public override long Position + { + get { return ReadPosition; } + set { throw new NotSupportedException(); } + } + public override bool CanTimeout { get { return UnderlyingStream.CanTimeout; } } + public override bool CanWrite { get { return true; } } + public override long Length + { + get + { + if (IsSourceCompressed) + { + return (long)DecompressedLength; + } + else + { + return UnderlyingStream.Length; + } + } + } + public override void SetLength(long value) { throw new NotSupportedException(); } + public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public override void Flush() { UnderlyingStream.Flush(); } + public override void Close() { UnderlyingStream.Close(); } + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.UnderlyingStream.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/GPT.cs b/GPT.cs new file mode 100644 index 0000000..0a25063 --- /dev/null +++ b/GPT.cs @@ -0,0 +1,721 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Xml.Serialization; + +namespace UnifiedFlashingPlatform +{ + [XmlType("Partitions")] + public class GPT + { + private byte[] GPTBuffer; + private readonly UInt32 HeaderOffset; + private readonly UInt32 HeaderSize; + private UInt32 TableOffset; + private UInt32 TableSize; + private readonly UInt32 PartitionEntrySize; + private readonly UInt32 MaxPartitions; + internal UInt64 FirstUsableSector; + internal UInt64 LastUsableSector; + internal bool HasChanged = false; + + [XmlElement("Partition")] + public List Partitions = new(); + + public GPT() // Only for serialization + { + } + + internal GPT(byte[] GPTBuffer) + { + this.GPTBuffer = GPTBuffer; + UInt32? TempHeaderOffset = ByteOperations.FindAscii(GPTBuffer, "EFI PART"); + if (TempHeaderOffset == null) + { + throw new WPinternalsException("Bad GPT", "The GPT read isn't valid. Couldn't find the text \"EFI PART\"."); + } + + HeaderOffset = (UInt32)TempHeaderOffset; + HeaderSize = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x0C); + TableOffset = HeaderOffset + 0x200; + FirstUsableSector = ByteOperations.ReadUInt64(GPTBuffer, HeaderOffset + 0x28); + LastUsableSector = ByteOperations.ReadUInt64(GPTBuffer, HeaderOffset + 0x30); + MaxPartitions = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x50); + PartitionEntrySize = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x54); + TableSize = MaxPartitions * PartitionEntrySize; + if ((TableOffset + TableSize) > GPTBuffer.Length) + { + throw new WPinternalsException("Bad GPT", "The GPT read isn't valid. The sizes defined in the GPT header exceed the provided GPT size."); + } + + UInt32 PartitionOffset = TableOffset; + + while (PartitionOffset < (TableOffset + TableSize)) + { + string Name = ByteOperations.ReadUnicodeString(GPTBuffer, PartitionOffset + 0x38, 0x48).TrimEnd(new char[] {(char)0, ' '}); + if (Name.Length == 0) + { + break; + } + + Partition CurrentPartition = new(); + CurrentPartition.Name = Name; + CurrentPartition.FirstSector = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x20); + CurrentPartition.LastSector = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x28); + CurrentPartition.PartitionTypeGuid = ByteOperations.ReadGuid(GPTBuffer, PartitionOffset + 0x00); + CurrentPartition.PartitionGuid = ByteOperations.ReadGuid(GPTBuffer, PartitionOffset + 0x10); + CurrentPartition.Attributes = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x30); + Partitions.Add(CurrentPartition); + PartitionOffset += PartitionEntrySize; + } + + HasChanged = false; + } + + internal Partition GetPartition(string Name) + { + return Partitions.Find(p => string.Equals(p.Name, Name, StringComparison.CurrentCultureIgnoreCase)); + } + + // Magic! + // SecureBoot hack for Bootloader Spec A starts here + internal byte[] InsertHack() + { + Partition HackPartition = Partitions.Find(p => p.Name == "HACK"); + Partition SBL1 = Partitions.Find(p => p.Name == "SBL1"); + Partition SBL2 = Partitions.Find(p => p.Name == "SBL2"); + + if ((SBL1 == null) || (SBL2 == null)) + { + throw new WPinternalsException("Bad GPT", "Can't patch GPT for the Secure Boot hack for Spec A devices. The provided GPT does not include a SBL1 and/or SBL2 partition."); + } + + if (HackPartition == null) + { + HackPartition = new Partition + { + Name = "HACK", + Attributes = SBL2.Attributes, + FirstSector = SBL1.LastSector, + LastSector = SBL1.LastSector, + + PartitionTypeGuid = SBL2.PartitionTypeGuid, + PartitionGuid = SBL2.PartitionGuid + }; + + Partitions.Add(HackPartition); + + SBL1.LastSector--; + + SBL2.PartitionTypeGuid = new Guid(new byte[] { 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74 }); + SBL2.PartitionGuid = new Guid(new byte[] { 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74 }); + } + + HasChanged = true; + + return Rebuild(); + } + + internal byte[] RemoveHack() + { + Partition HackPartition = Partitions.Find(p => p.Name == "HACK"); + Partition SBL1 = Partitions.Find(p => p.Name == "SBL1"); + Partition SBL2 = Partitions.Find(p => p.Name == "SBL2"); + + if ((SBL1 == null) || (SBL2 == null)) + { + throw new WPinternalsException("Bad GPT", "Can't un-patch GPT for the Secure Boot hack for Spec A devices. The provided GPT does not include a SBL1 and/or SBL2 partition."); + } + + if (HackPartition != null) + { + SBL2.PartitionTypeGuid = HackPartition.PartitionTypeGuid; + SBL2.PartitionGuid = HackPartition.PartitionGuid; + + Partitions.Remove(HackPartition); + + SBL1.LastSector++; + } + + HasChanged = true; + + return Rebuild(); + } + + internal byte[] Rebuild() + { + if (GPTBuffer == null) + { + TableSize = 0x4200; + TableOffset = 0; + GPTBuffer = new byte[TableSize]; + } + else + { + Array.Clear(GPTBuffer, (int)TableOffset, (int)TableSize); + } + + UInt32 PartitionOffset = TableOffset; + foreach (Partition CurrentPartition in Partitions) + { + ByteOperations.WriteGuid(GPTBuffer, PartitionOffset + 0x00, CurrentPartition.PartitionTypeGuid); + ByteOperations.WriteGuid(GPTBuffer, PartitionOffset + 0x10, CurrentPartition.PartitionGuid); + ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x20, CurrentPartition.FirstSector); + ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x28, CurrentPartition.LastSector); + ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x30, CurrentPartition.Attributes); + ByteOperations.WriteUnicodeString(GPTBuffer, PartitionOffset + 0x38, CurrentPartition.Name, 0x48); + + PartitionOffset += PartitionEntrySize; + } + + ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x58, ByteOperations.CRC32(GPTBuffer, TableOffset, TableSize)); + ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x10, 0); + ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x10, ByteOperations.CRC32(GPTBuffer, HeaderOffset, HeaderSize)); + + return GPTBuffer; + } + + internal void MergePartitionsFromFile(string Path, bool RoundToChunks) + { + MergePartitions(File.ReadAllText(Path), RoundToChunks); + } + + internal void MergePartitionsFromStream(Stream Partitions, bool RoundToChunks) + { + using TextReader tr = new StreamReader(Partitions); + MergePartitions(tr.ReadToEnd(), RoundToChunks); + } + + internal void MergePartitions(string Xml, bool RoundToChunks, ZipArchive Archive = null) + { + GPT GptToMerge; + if (Xml == null) + { + GptToMerge = new GPT(); + } + else + { + XmlSerializer x = new(typeof(GPT), ""); + MemoryStream s = new(System.Text.Encoding.ASCII.GetBytes(Xml)); + GptToMerge = (GPT)x.Deserialize(s); + s.Dispose(); + } + + if (Archive != null) + { + foreach (Partition NewPartition in GptToMerge.Partitions) + { + ZipArchiveEntry Entry = Archive.Entries.FirstOrDefault(e => string.Equals(e.Name, NewPartition.Name, StringComparison.CurrentCultureIgnoreCase) || e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))); + if (Entry == null) + { + // There is a partition entry in the xml, but this partition is not present in the archive. + + Partition OldPartition = GetPartition(NewPartition.Name); + if (OldPartition == null) + { + // The partition entry in the xml is also not present in the current partition table. + // It must have a know position and length. + + if (NewPartition.LastSector == 0) + { + throw new WPinternalsException("Unknown length for partition \"" + NewPartition.Name + "\". The last sector property is set to 0 and the partition doesn't exist on the device currently."); + } + } + else + { + // The partition entry in the xml is also present in the current partition table. + // But since the partition is not present in the archive, the partition cannot be relocated. + // If the location of the new partition is specified, it must be the same as the current partition. + + if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != OldPartition.FirstSector)) + { + throw new WPinternalsException("Incorrect location for partition \"" + NewPartition.Name + "\". A partition defined in the xml file got its boundaries updated, but as the partition isn't provided in the archive, it is not possible to relocate it."); + } + + if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector)) + { + throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\". A partition defined in the xml file got its boundaries updated, but as the partition isn't provided in the archive, it is not possible to relocate it."); + } + + NewPartition.FirstSector = OldPartition.FirstSector; + NewPartition.LastSector = OldPartition.LastSector; + } + } + else + { + // The partition in the xml is also present in the archive. + // If the length is specified in the xml, it must match the file in the archive. + + ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200; + using (DecompressedStream DecompressedStream = new(Entry.Open())) + { + try + { + StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200; + } + catch { } + } + + if (NewPartition.LastSector == 0) + { + NewPartition.SizeInSectors = StreamLengthInSectors; + } + else + { + if (NewPartition.SizeInSectors != StreamLengthInSectors) + { + throw new WPinternalsException("Inconsistent length specified for partition \"" + NewPartition.Name + "\". The provided partition in the archive does not match the length specified in the xml file."); + } + } + } + } + } + else + { + foreach (Partition NewPartition in GptToMerge.Partitions) + { + // This is a partition entry in the xml, and there is no archive. + + Partition OldPartition = GetPartition(NewPartition.Name); + if (OldPartition == null) + { + // The partition entry in the xml is also not present in the current partition table. + // It must have a known position and length. + + if (NewPartition.LastSector == 0) + { + throw new WPinternalsException("Unknown length for partition \"" + NewPartition.Name + "\". The last sector property is set to 0 and the partition doesn't exist on the device currently."); + } + } + else + { + // The partition entry in the xml is also present in the current partition table. + // But since the partition is not present in the archive, the partition cannot be relocated. + // If the location of the new partition is specified, it must be the same as the current partition. + + if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != OldPartition.FirstSector)) + { + throw new WPinternalsException("Incorrect location for partition \"" + NewPartition.Name + "\". A partition defined in the xml file got its boundaries updated, but as the partition isn't provided in the archive, it is not possible to relocate it."); + } + + if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector)) + { + throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\". A partition defined in the xml file got its boundaries updated, but as the partition isn't provided in the archive, it is not possible to relocate it."); + } + + NewPartition.FirstSector = OldPartition.FirstSector; + NewPartition.LastSector = OldPartition.LastSector; + } + } + } + + List DynamicPartitions = new(); + if (Archive != null) + { + // Partitions which are present in the archive, and which have no start-sector in the new GPT data (dynamic relocation), + // and which can be clustered to the end of emmc, are first removed from the existing GPT. + IEnumerable SortedPartitions = Partitions.OrderBy(p => p.FirstSector); + for (int i = SortedPartitions.Count() - 1; i >= 0; i--) + { + Partition OldPartition = SortedPartitions.ElementAt(i); + + // Present in archive? + ZipArchiveEntry Entry = Archive.Entries.FirstOrDefault(e => string.Equals(e.Name, OldPartition.Name, StringComparison.CurrentCultureIgnoreCase) || e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))); + if (Entry != null) + { + // Not present in new GPT or present in GPT without FirstSector? + Partition NewPartition = GptToMerge.GetPartition(OldPartition.Name); + if ((NewPartition == null) || (NewPartition.FirstSector == 0)) + { + DynamicPartitions.Insert(0, OldPartition); + this.Partitions.Remove(OldPartition); + } + else + { + break; + } + } + else + { + break; + } + } + } + + // All partitions in the new GPT data should have a start-sector and end-sector by now. + // The partitions in the new GPT data will be applied to the current partition-table. + // Existing partitions, which are overwritten by the new partitions will be removed from the existing GPT. + // Existing partition with the same name in the existing GPT is reused (guids and attribs remain, if not specified). + UInt64 LowestSector = 0; + Partition DPP = this.GetPartition("DPP"); + if (DPP != null) + { + LowestSector = DPP.LastSector + 1; + } + + foreach (Partition NewPartition in GptToMerge.Partitions) + { + // If the new partition is a dynamic partition, then skip it here. It will be added later. + if (DynamicPartitions.Select(p => p.Name).Any(n => string.Equals(n, NewPartition.Name, StringComparison.CurrentCultureIgnoreCase))) + { + continue; + } + + // Sanity check + if (NewPartition.FirstSector < LowestSector) + { + throw new WPinternalsException("Bad sector alignment for partition: " + NewPartition.Name + ". The partition is located before DPP."); + } + + Partition CurrentPartition = this.GetPartition(NewPartition.Name); + if (CurrentPartition == null) + { + CurrentPartition = new Partition + { + Name = NewPartition.Name + }; + this.Partitions.Add(CurrentPartition); + HasChanged = true; + } + + if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != CurrentPartition.FirstSector)) + { + CurrentPartition.FirstSector = NewPartition.FirstSector; + HasChanged = true; + } + if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != CurrentPartition.LastSector)) + { + CurrentPartition.LastSector = NewPartition.LastSector; + HasChanged = true; + } + if ((NewPartition.Attributes != 0) && (CurrentPartition.Attributes != NewPartition.Attributes)) + { + CurrentPartition.Attributes = NewPartition.Attributes; + HasChanged = true; + } + + if ((NewPartition.PartitionGuid != Guid.Empty) || (NewPartition.PartitionGuid != CurrentPartition.PartitionGuid)) + { + HasChanged = true; + } + + if (NewPartition.PartitionGuid != Guid.Empty) + { + CurrentPartition.PartitionGuid = NewPartition.PartitionGuid; + } + + if (CurrentPartition.PartitionGuid != Guid.Empty) + { + CurrentPartition.PartitionGuid = Guid.NewGuid(); + } + + if ((NewPartition.PartitionTypeGuid != Guid.Empty) || (NewPartition.PartitionTypeGuid != CurrentPartition.PartitionTypeGuid)) + { + HasChanged = true; + } + + if (NewPartition.PartitionTypeGuid != Guid.Empty) + { + CurrentPartition.PartitionTypeGuid = NewPartition.PartitionTypeGuid; + } + + if (CurrentPartition.PartitionTypeGuid != Guid.Empty) + { + CurrentPartition.PartitionTypeGuid = Guid.NewGuid(); + } + + for (int i = this.Partitions.Count - 1; i >= 0; i--) + { + if (this.Partitions[i] != CurrentPartition && (CurrentPartition.FirstSector <= this.Partitions[i].LastSector) && (CurrentPartition.LastSector >= this.Partitions[i].FirstSector)) + { + this.Partitions.RemoveAt(i); + HasChanged = true; + } + } + } + + if (Archive != null) + { + // All partitions listed in the archive, which are present in the existing GPT, should overwrite the existing partition. + // Check if the sizes of the partitions in the archive do not exceed the size of the partition, as listed in the GPT. + foreach (Partition OldPartition in this.Partitions) + { + ZipArchiveEntry Entry = Archive.Entries.FirstOrDefault(e => string.Equals(e.Name, OldPartition.Name, StringComparison.CurrentCultureIgnoreCase) || e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))); + if (Entry != null) + { + DecompressedStream DecompressedStream = new(Entry.Open()); + ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200; + try + { + StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200; + } + catch { } + DecompressedStream.Close(); + + UInt64 MaxPartitionSizeInSectors = OldPartition.SizeInSectors; + Partition NextPartition = this.Partitions.Where(p => p.FirstSector > OldPartition.FirstSector).OrderBy(p => p.FirstSector).FirstOrDefault(); + if (NextPartition != null) + { + MaxPartitionSizeInSectors = NextPartition.FirstSector - OldPartition.FirstSector; + } + + if (StreamLengthInSectors > MaxPartitionSizeInSectors) + { + throw new WPinternalsException("Incorrect length for partition \"" + OldPartition.Name + "\". The provided partition in the archive does not match the length specified in the xml file."); + } + + if (OldPartition.SizeInSectors != StreamLengthInSectors) + { + OldPartition.SizeInSectors = StreamLengthInSectors; + HasChanged = true; + } + } + } + + // All remaining partitions in the archive, which were listed in the original GPT, + // should be added at the end of the partition-table. + UInt64 FirstFreeSector = 0x5000; + if (this.Partitions.Count > 0) + { + FirstFreeSector = this.Partitions.Max(p => p.LastSector) + 1; + + // Always start a new partition on a new chunk (0x100 sector boundary), to be more flexible during custom flash + if (RoundToChunks && (((double)FirstFreeSector % 0x100) != 0)) + { + FirstFreeSector = (UInt64)(Math.Ceiling((double)FirstFreeSector / 0x100) * 0x100); + } + } + foreach (Partition NewPartition in DynamicPartitions) + { + if (NewPartition.FirstSector != FirstFreeSector) + { + NewPartition.FirstSector = FirstFreeSector; + HasChanged = true; + } + ZipArchiveEntry Entry = Archive.Entries.FirstOrDefault(e => string.Equals(e.Name, NewPartition.Name, StringComparison.CurrentCultureIgnoreCase) || e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))); + DecompressedStream DecompressedStream = new(Entry.Open()); + ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200; + try + { + StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200; + } + catch { } + DecompressedStream.Close(); + if (NewPartition.SizeInSectors != StreamLengthInSectors) + { + NewPartition.SizeInSectors = StreamLengthInSectors; + HasChanged = true; + } + this.Partitions.Add(NewPartition); + FirstFreeSector += StreamLengthInSectors; + + // Always start a new partition on a new chunk (0x100 sector boundary), to be more flexible during custom flash + if (RoundToChunks && (((double)FirstFreeSector % 0x100) != 0)) + { + FirstFreeSector = (UInt64)(Math.Ceiling((double)FirstFreeSector / 0x100) * 0x100); + } + } + } + + Rebuild(); + } + + internal void WritePartitions(string Path) + { + string DirPath = System.IO.Path.GetDirectoryName(Path); + if (!string.IsNullOrEmpty(DirPath) && !Directory.Exists(DirPath)) + { + Directory.CreateDirectory(DirPath); + } + + XmlSerializer x = new(typeof(GPT), ""); + + XmlSerializerNamespaces ns = new(); + ns.Add("", ""); + StreamWriter FileWriter = new(Path); + x.Serialize(FileWriter, this, ns); + FileWriter.Close(); + } + + internal void WritePartitions(Stream Stream) + { + XmlSerializer x = new(typeof(GPT), ""); + + XmlSerializerNamespaces ns = new(); + ns.Add("", ""); + x.Serialize(Stream, this, ns); + } + + internal static GPT ReadPartitions(string Path) + { + XmlSerializer x = new(typeof(GPT), ""); + using FileStream s = new(Path, FileMode.Open); + return (GPT)x.Deserialize(s); + } + + internal void RestoreBackupPartitions() + { + // This is necessary, because the partitions and backup-partitions can exchange. + // This may cause the startsector to be higher than the maximum allowed sector for flashing with a Lumia V1 programmer (hardcoded in programmer) + foreach (string RevisePartitionName in (List)(new(new string[] { "SBL1", "SBL2", "SBL3", "UEFI", "TZ", "RPM", "WINSECAPP" }))) + { + Partition RevisePartition = GetPartition(RevisePartitionName); + Partition ReviseBackupPartition = GetPartition("BACKUP_" + RevisePartitionName); + if ((RevisePartition != null) && (ReviseBackupPartition != null) && (RevisePartition.FirstSector > ReviseBackupPartition.FirstSector)) + { + ulong OriginalFirstSector = RevisePartition.FirstSector; + ulong OriginalLastSector = RevisePartition.LastSector; + RevisePartition.FirstSector = ReviseBackupPartition.FirstSector; + RevisePartition.LastSector = ReviseBackupPartition.LastSector; + ReviseBackupPartition.FirstSector = OriginalFirstSector; + ReviseBackupPartition.LastSector = OriginalLastSector; + + HasChanged = true; + } + + if (RevisePartition.LastSector >= 0xF400) + { + throw new WPinternalsException("Unsupported partition layout!", "The last sector of one of the BACKUP partitions defined in GPT exceeds the maximum threshold expected in order to restore BACKUP partitions to the device."); + } + } + } + } + + public class Partition + { + private UInt64 _SizeInSectors; + private UInt64 _FirstSector; + private UInt64 _LastSector; + + public string Name; // 0x48 + public Guid PartitionTypeGuid; // 0x10 + public Guid PartitionGuid; // 0x10 + [XmlIgnore] + internal UInt64 Attributes; // 0x08 + + [XmlIgnore] + internal UInt64 SizeInSectors + { + get + { + if (_SizeInSectors != 0) + { + return _SizeInSectors; + } + else + { + return LastSector - FirstSector + 1; + } + } + set + { + _SizeInSectors = value; + if (FirstSector != 0) + { + LastSector = FirstSector + _SizeInSectors - 1; + } + } + } + + [XmlIgnore] + internal UInt64 FirstSector // 0x08 + { + get + { + return _FirstSector; + } + set + { + _FirstSector = value; + if (_SizeInSectors != 0) + { + _LastSector = FirstSector + _SizeInSectors - 1; + } + } + } + + [XmlIgnore] + internal UInt64 LastSector // 0x08 + { + get + { + return _LastSector; + } + set + { + _LastSector = value; + _SizeInSectors = 0; + } + } + + [XmlIgnore] + public string Volume + { + get + { + return @"\\?\Volume" + PartitionGuid.ToString("b") + @"\"; + } + } + + [XmlElement(ElementName = "FirstSector")] + public string FirstSectorAsString + { + get + { + return "0x" + FirstSector.ToString("X16"); + } + set + { + FirstSector = Convert.ToUInt64(value, 16); + } + } + + [XmlElement(ElementName = "LastSector")] + public string LastSectorAsString + { + get + { + return "0x" + LastSector.ToString("X16"); + } + set + { + LastSector = Convert.ToUInt64(value, 16); + } + } + + [XmlElement(ElementName = "Attributes")] + public string AttributesAsString + { + get + { + return "0x" + Attributes.ToString("X16"); + } + set + { + Attributes = Convert.ToUInt64(value, 16); + } + } + } +} \ No newline at end of file diff --git a/ReadSeekableStream.cs b/ReadSeekableStream.cs new file mode 100644 index 0000000..35d1c59 --- /dev/null +++ b/ReadSeekableStream.cs @@ -0,0 +1,288 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; + +namespace UnifiedFlashingPlatform +{ + // This class is written by: Eugene Beresovsky + // https://stackoverflow.com/questions/13035925/stream-wrapper-to-make-stream-seekable/28036366#28036366 + internal class ReadSeekableStream : Stream + { + private long _underlyingPosition; + private readonly byte[] _seekBackBuffer; + private int _seekBackBufferCount; + private int _seekBackBufferIndex; + private readonly Stream _underlyingStream; + + public ReadSeekableStream(Stream underlyingStream, int seekBackBufferSize) + { + if (!underlyingStream.CanRead) + { + throw new Exception("Provided stream " + underlyingStream + " is not readable"); + } + + _underlyingStream = underlyingStream; + _seekBackBuffer = new byte[seekBackBufferSize]; + } + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return true; } } + + public override int Read(byte[] buffer, int offset, int count) + { + int copiedFromBackBufferCount = 0; + if (_seekBackBufferIndex < _seekBackBufferCount) + { + copiedFromBackBufferCount = Math.Min(count, _seekBackBufferCount - _seekBackBufferIndex); + Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferIndex, buffer, offset, copiedFromBackBufferCount); + offset += copiedFromBackBufferCount; + count -= copiedFromBackBufferCount; + _seekBackBufferIndex += copiedFromBackBufferCount; + } + int bytesReadFromUnderlying = 0; + if (count > 0) + { + bytesReadFromUnderlying = _underlyingStream.Read(buffer, offset, count); + if (bytesReadFromUnderlying > 0) + { + _underlyingPosition += bytesReadFromUnderlying; + + var copyToBufferCount = Math.Min(bytesReadFromUnderlying, _seekBackBuffer.Length); + var copyToBufferOffset = Math.Min(_seekBackBufferCount, _seekBackBuffer.Length - copyToBufferCount); + var bufferBytesToMove = Math.Min(_seekBackBufferCount - 1, copyToBufferOffset); + + if (bufferBytesToMove > 0) + { + Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferCount - bufferBytesToMove, _seekBackBuffer, 0, bufferBytesToMove); + } + + Buffer.BlockCopy(buffer, offset, _seekBackBuffer, copyToBufferOffset, copyToBufferCount); + _seekBackBufferCount = Math.Min(_seekBackBuffer.Length, _seekBackBufferCount + copyToBufferCount); + _seekBackBufferIndex = _seekBackBufferCount; + } + } + return copiedFromBackBufferCount + bytesReadFromUnderlying; + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.End) + { + return SeekFromEnd((int)Math.Max(0, -offset)); + } + + var relativeOffset = origin == SeekOrigin.Current + ? offset + : offset - Position; + + if (relativeOffset == 0) + { + return Position; + } + else if (relativeOffset > 0) + { + return SeekForward(relativeOffset); + } + else + { + return SeekBackwards(-relativeOffset); + } + } + + private long SeekForward(long origOffset) + { + long offset = origOffset; + var seekBackBufferLength = _seekBackBuffer.Length; + + int backwardSoughtBytes = _seekBackBufferCount - _seekBackBufferIndex; + int seekForwardInBackBuffer = (int)Math.Min(offset, backwardSoughtBytes); + offset -= seekForwardInBackBuffer; + _seekBackBufferIndex += seekForwardInBackBuffer; + + if (offset > 0) + { + // first completely fill seekBackBuffer to remove special cases from while loop below + if (_seekBackBufferCount < seekBackBufferLength) + { + var maxRead = seekBackBufferLength - _seekBackBufferCount; + if (offset < maxRead) + { + maxRead = (int)offset; + } + + var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); + _underlyingPosition += bytesRead; + _seekBackBufferCount += bytesRead; + _seekBackBufferIndex = _seekBackBufferCount; + if (bytesRead < maxRead) + { + if (_seekBackBufferCount < offset) + { + throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); + } + + return Position; + } + offset -= bytesRead; + } + + // now alternate between filling tempBuffer and seekBackBuffer + bool fillTempBuffer = true; + var tempBuffer = new byte[seekBackBufferLength]; + while (offset > 0) + { + var maxRead = offset < seekBackBufferLength ? (int)offset : seekBackBufferLength; + var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, maxRead); + _underlyingPosition += bytesRead; + var bytesReadDiff = maxRead - bytesRead; + offset -= bytesRead; + if (bytesReadDiff > 0 /* reached end-of-stream */ || offset == 0) + { + if (fillTempBuffer) + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + } + else + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + + Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + } + if (offset > 0) + { + throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); + } + } + fillTempBuffer = !fillTempBuffer; + } + } + return Position; + } + + private long SeekBackwards(long offset) + { + var intOffset = (int)offset; + if (offset > int.MaxValue || intOffset > _seekBackBufferIndex) + { + throw new NotSupportedException("Cannot currently seek backwards more than " + _seekBackBufferIndex + " bytes"); + } + + _seekBackBufferIndex -= intOffset; + return Position; + } + + private long SeekFromEnd(long offset) + { + var intOffset = (int)offset; + var seekBackBufferLength = _seekBackBuffer.Length; + if (offset > int.MaxValue || intOffset > seekBackBufferLength) + { + throw new NotSupportedException("Cannot seek backwards from end more than " + seekBackBufferLength + " bytes"); + } + + // first completely fill seekBackBuffer to remove special cases from while loop below + if (_seekBackBufferCount < seekBackBufferLength) + { + var maxRead = seekBackBufferLength - _seekBackBufferCount; + var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); + _underlyingPosition += bytesRead; + _seekBackBufferCount += bytesRead; + _seekBackBufferIndex = Math.Max(0, _seekBackBufferCount - intOffset); + if (bytesRead < maxRead) + { + if (_seekBackBufferCount < intOffset) + { + throw new NotSupportedException("Could not seek backwards from end " + intOffset + " bytes"); + } + + return Position; + } + } + else + { + _seekBackBufferIndex = _seekBackBufferCount; + } + + // now alternate between filling tempBuffer and seekBackBuffer + bool fillTempBuffer = true; + var tempBuffer = new byte[seekBackBufferLength]; + while (true) + { + var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, seekBackBufferLength); + _underlyingPosition += bytesRead; + var bytesReadDiff = seekBackBufferLength - bytesRead; + if (bytesReadDiff > 0) // reached end-of-stream + { + if (fillTempBuffer) + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + } + else + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + + Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + } + _seekBackBufferIndex -= intOffset; + return Position; + } + fillTempBuffer = !fillTempBuffer; + } + } + + public override long Position + { + get { return _underlyingPosition - (_seekBackBufferCount - _seekBackBufferIndex); } + set { Seek(value, SeekOrigin.Begin); } + } + + public override bool CanTimeout { get { return _underlyingStream.CanTimeout; } } + public override bool CanWrite { get { return _underlyingStream.CanWrite; } } + public override long Length { get { return _underlyingStream.Length; } } + public override void SetLength(long value) { _underlyingStream.SetLength(value); } + public override void Write(byte[] buffer, int offset, int count) { _underlyingStream.Write(buffer, offset, count); } + public override void Flush() { _underlyingStream.Flush(); } + public override void Close() { _underlyingStream.Close(); } + public new void Dispose() { _underlyingStream.Dispose(); } + protected override void Dispose(bool disposing) + { + if (disposing) + { + _underlyingStream.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/UnifiedFlashingPlatformTransport.Commands.cs b/UnifiedFlashingPlatformTransport.Commands.cs new file mode 100644 index 0000000..927a886 --- /dev/null +++ b/UnifiedFlashingPlatformTransport.Commands.cs @@ -0,0 +1,79 @@ +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformTransport + { + // + // Not valid commands + // + /* NOK */ + private static string Signature = "NOK"; + /* NOKX */ + private static string ExtendedMessageSignature = Signature + "X"; + /* NOKXC */ + private static string CommonExtendedMessageSignature = ExtendedMessageSignature + "C"; + /* NOKXF */ + private static string UFPExtendedMessageSignature = ExtendedMessageSignature + "F"; + + // + // Normal commands + // + /* NOKF */ + private static string FlashSignature = Signature + "F"; + /* NOKI */ + private static string HelloSignature = Signature + "I"; + /* NOKM */ + private static string MassStorageSignature = Signature + "M"; + /* NOKN */ + private static string TelemetryEndSignature = Signature + "N"; + /* NOKR */ + private static string RebootSignature = Signature + "R"; + /* NOKS */ + private static string TelemetryStartSignature = Signature + "S"; + /* NOKT */ + private static string GetGPTSignature = Signature + "T"; + /* NOKV */ + private static string InfoQuerySignature = Signature + "V"; + /* NOKZ */ + private static string ShutdownSignature = Signature + "Z"; + + // + // Common extended commands + // + /* NOKXCB */ + private static string SwitchModeSignature = CommonExtendedMessageSignature + "B"; + /* NOKXCC */ + private static string ClearScreenSignature = CommonExtendedMessageSignature + "C"; + /* NOKXCD */ + private static string GetDirectoryEntriesSignature = CommonExtendedMessageSignature + "D"; + /* NOKXCE */ + private static string EchoSignature = CommonExtendedMessageSignature + "E"; + /* NOKXCF */ + private static string GetFileSignature = CommonExtendedMessageSignature + "F"; + /* NOKXCM */ + private static string DisplayCustomMessageSignature = CommonExtendedMessageSignature + "M"; + /* NOKXCP */ + private static string PutFileSignature = CommonExtendedMessageSignature + "P"; + /* NOKXCT */ + private static string BenchmarkTestsSignature = CommonExtendedMessageSignature + "T"; + + // + // UFP extended commands + // + /* NOKXFF */ + private static string AsyncFlashModeSignature = UFPExtendedMessageSignature + "F"; + /* NOKXFI */ + private static string UnlockSignature = UFPExtendedMessageSignature + "I"; + /* NOKXFO */ + private static string RelockSignature = UFPExtendedMessageSignature + "O"; + /* NOKXFR */ + private static string ReadParamSignature = UFPExtendedMessageSignature + "R"; + /* NOKXFS */ + private static string SecureFlashSignature = UFPExtendedMessageSignature + "S"; + /* NOKXFT */ + private static string TelemetryReadSignature = UFPExtendedMessageSignature + "T"; + /* NOKXFW */ + private static string WriteParamSignature = UFPExtendedMessageSignature + "W"; + /* NOKXFX */ + private static string GetLogsSignature = UFPExtendedMessageSignature + "X"; + } +} diff --git a/UnifiedFlashingPlatformTransport.WPI.cs b/UnifiedFlashingPlatformTransport.WPI.cs new file mode 100644 index 0000000..143a57b --- /dev/null +++ b/UnifiedFlashingPlatformTransport.WPI.cs @@ -0,0 +1,126 @@ +using System; +using System.Diagnostics; + +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformTransport + { + public void FlashSectors(UInt32 StartSector, byte[] Data, int Progress = 0) + { + // Start sector is in UInt32, so max size of eMMC is 2 TB. + + byte[] Request = new byte[Data.Length + 0x40]; + + string Header = FlashSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x05] = 0; // Device type = 0 + Buffer.BlockCopy(BigEndian.GetBytes(StartSector, 4), 0, Request, 0x0B, 4); // Start sector + Buffer.BlockCopy(BigEndian.GetBytes(Data.Length / 0x200, 4), 0, Request, 0x0F, 4); // Sector count + Request[0x13] = (byte)Progress; // Progress (0 - 100) + Request[0x18] = 0; // Do Verify + Request[0x19] = 0; // Is Test + + Buffer.BlockCopy(Data, 0, Request, 0x40, Data.Length); + + ExecuteRawMethod(Request); + } + + internal void Hello() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, HelloSignature); + byte[] Response = ExecuteRawMethod(Request); + if (Response == null) + { + throw new BadConnectionException(); + } + + if (ByteOperations.ReadAsciiString(Response, 0, 4) != HelloSignature) + { + throw new WPinternalsException("Bad response from phone!", "The phone did not answer properly to the Hello message sent."); + } + } + + /* NOKM */ + /* NOKN */ + + internal void ResetPhone() + { + Debug.WriteLine("Rebooting phone"); + try + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, RebootSignature); + ExecuteRawVoidMethod(Request); + } + catch + { + Debug.WriteLine("Sending reset-request failed"); + Debug.WriteLine("Assuming automatic reset already in progress"); + } + } + + /* NOKS */ + + internal GPT ReadGPT() + { + // If this function is used with a locked BootMgr v1, + // then the mode-switching should be done outside this function, + // because the context-switches that are used here are not supported on BootMgr v1. + + // Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!! + + /*PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false); + FlashAppType OriginalAppType = Info.App; + bool Switch = (Info.App != FlashAppType.BootManager) && Info.IsBootloaderSecure; + if (Switch) + { + SwitchToBootManagerContext(); + }*/ + + byte[] Request = new byte[0x04]; + string Header = GetGPTSignature; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + } + + byte[] GPTBuffer = new byte[Buffer.Length - 0x208]; + System.Buffer.BlockCopy(Buffer, 0x208, GPTBuffer, 0, 0x4200); + + /*if (Switch) + { + if (OriginalAppType == FlashAppType.FlashApp) + { + SwitchToFlashAppContext(); + } + else + { + SwitchToPhoneInfoAppContext(); + } + }*/ + + return new GPT(GPTBuffer); // NOKT message header and MBR are ignored + } + + /* NOKV */ + + public void Shutdown() + { + byte[] Request = new byte[4]; + string Header = ShutdownSignature; + Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawMethod(Request); + } + } +} diff --git a/UnifiedFlashingPlatformTransport.cs b/UnifiedFlashingPlatformTransport.cs index e3e14ee..6beedca 100644 --- a/UnifiedFlashingPlatformTransport.cs +++ b/UnifiedFlashingPlatformTransport.cs @@ -24,12 +24,10 @@ using MadWizard.WinUSBNet; using System; using System.Linq; -using System.Numerics; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace UnifiedFlashingPlatform { - public class UnifiedFlashingPlatformTransport : IDisposable + public partial class UnifiedFlashingPlatformTransport : IDisposable { private bool Disposed = false; private readonly USBDevice USBDevice = null; @@ -60,51 +58,6 @@ namespace UnifiedFlashingPlatform } } - // - // Not valid commands - // - /* NOK */ private static string Signature = "NOK"; - /* NOKX */ private static string ExtendedMessageSignature = Signature + "X"; - /* NOKXC */ private static string CommonExtendedMessageSignature = ExtendedMessageSignature + "C"; - /* NOKXF */ private static string UFPExtendedMessageSignature = ExtendedMessageSignature + "F"; - - // - // Normal commands - // - /* NOKF */ private static string FlashSignature = Signature + "F"; - /* NOKI */ private static string HelloSignature = Signature + "I"; - /* NOKM */ private static string MassStorageSignature = Signature + "M"; - /* NOKN */ private static string TelemetryEndSignature = Signature + "N"; - /* NOKR */ private static string RebootSignature = Signature + "R"; - /* NOKS */ private static string TelemetryStartSignature = Signature + "S"; - /* NOKT */ private static string GetGPTSignature = Signature + "T"; - /* NOKV */ private static string InfoQuerySignature = Signature + "V"; - /* NOKZ */ private static string ShutdownSignature = Signature + "Z"; - - // - // Common extended commands - // - /* NOKXCB */ private static string SwitchModeSignature = CommonExtendedMessageSignature + "B"; - /* NOKXCC */ private static string ClearScreenSignature = CommonExtendedMessageSignature + "C"; - /* NOKXCD */ private static string GetDirectoryEntriesSignature = CommonExtendedMessageSignature + "D"; - /* NOKXCE */ private static string EchoSignature = CommonExtendedMessageSignature + "E"; - /* NOKXCF */ private static string GetFileSignature = CommonExtendedMessageSignature + "F"; - /* NOKXCM */ private static string DisplayCustomMessageSignature = CommonExtendedMessageSignature + "M"; - /* NOKXCP */ private static string PutFileSignature = CommonExtendedMessageSignature + "P"; - /* NOKXCT */ private static string BenchmarkTestsSignature = CommonExtendedMessageSignature + "T"; - - // - // UFP extended commands - // - /* NOKXFF */ private static string AsyncFlashModeSignature = UFPExtendedMessageSignature + "F"; - /* NOKXFI */ private static string UnlockSignature = UFPExtendedMessageSignature + "I"; - /* NOKXFO */ private static string RelockSignature = UFPExtendedMessageSignature + "O"; - /* NOKXFR */ private static string ReadParamSignature = UFPExtendedMessageSignature + "R"; - /* NOKXFS */ private static string SecureFlashSignature = UFPExtendedMessageSignature + "S"; - /* NOKXFT */ private static string TelemetryReadSignature = UFPExtendedMessageSignature + "T"; - /* NOKXFW */ private static string WriteParamSignature = UFPExtendedMessageSignature + "W"; - /* NOKXFX */ private static string GetLogsSignature = UFPExtendedMessageSignature + "X"; - public byte[] ExecuteRawMethod(byte[] RawMethod) { return ExecuteRawMethod(RawMethod, RawMethod.Length); @@ -225,7 +178,7 @@ namespace UnifiedFlashingPlatform ExecuteRawMethod(Request); } - public void Shutdown() + public void ExtendedShutdown() { byte[] Request = new byte[7]; string Header = SwitchModeSignature + "Z"; @@ -233,7 +186,7 @@ namespace UnifiedFlashingPlatform ExecuteRawVoidMethod(Request); } - public void ResetPhone() + public void ExtendedResetPhone() { byte[] Request = new byte[7]; string Header = SwitchModeSignature + "R"; diff --git a/WPInternalsException.cs b/WPInternalsException.cs new file mode 100644 index 0000000..188b68a --- /dev/null +++ b/WPInternalsException.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2018, Rene Lergner - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace UnifiedFlashingPlatform +{ + internal class WPinternalsException : Exception + { + // Message and SubMessaage are always printable + internal string SubMessage = null; + + internal WPinternalsException() : base() { } + + internal WPinternalsException(string Message) : base(Message) { } + + internal WPinternalsException(string Message, Exception InnerException) : base(Message, InnerException) { } + + internal WPinternalsException(string Message, string SubMessage) : base(Message) { this.SubMessage = SubMessage; } + + internal WPinternalsException(string Message, string SubMessage, Exception InnerException) : base(Message, InnerException) { this.SubMessage = SubMessage; } + } +} \ No newline at end of file