mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
645 lines
29 KiB
C#
645 lines
29 KiB
C#
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @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 WPinternals
|
|
{
|
|
[XmlType("Partitions")]
|
|
public class GPT
|
|
{
|
|
private byte[] GPTBuffer;
|
|
private UInt32 HeaderOffset;
|
|
private UInt32 HeaderSize;
|
|
private UInt32 TableOffset;
|
|
private UInt32 TableSize;
|
|
private UInt32 PartitionEntrySize;
|
|
private UInt32 MaxPartitions;
|
|
internal UInt64 FirstUsableSector;
|
|
internal UInt64 LastUsableSector;
|
|
internal bool HasChanged = false;
|
|
|
|
[XmlElement("Partition")]
|
|
public List<Partition> Partitions = new List<Partition>();
|
|
|
|
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");
|
|
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");
|
|
|
|
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 Partition();
|
|
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.Where(p => (string.Compare(p.Name, Name, true) == 0)).FirstOrDefault();
|
|
}
|
|
|
|
// Magic!
|
|
// SecureBoot hack for Bootloader Spec A starts here
|
|
internal byte[] InsertHack()
|
|
{
|
|
Partition HackPartition = Partitions.Where(p => (p.Name == "HACK")).FirstOrDefault();
|
|
Partition SBL1 = Partitions.Where(p => (p.Name == "SBL1")).FirstOrDefault();
|
|
Partition SBL2 = Partitions.Where(p => (p.Name == "SBL2")).FirstOrDefault();
|
|
|
|
if ((SBL1 == null) || (SBL2 == null))
|
|
throw new WPinternalsException("Bad GPT");
|
|
|
|
if (HackPartition == null)
|
|
{
|
|
HackPartition = new Partition();
|
|
HackPartition.Name = "HACK";
|
|
HackPartition.Attributes = SBL2.Attributes;
|
|
HackPartition.FirstSector = SBL1.LastSector;
|
|
HackPartition.LastSector = SBL1.LastSector;
|
|
|
|
HackPartition.PartitionTypeGuid = SBL2.PartitionTypeGuid;
|
|
HackPartition.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.Where(p => (p.Name == "HACK")).FirstOrDefault();
|
|
Partition SBL1 = Partitions.Where(p => (p.Name == "SBL1")).FirstOrDefault();
|
|
Partition SBL2 = Partitions.Where(p => (p.Name == "SBL2")).FirstOrDefault();
|
|
|
|
if ((SBL1 == null) || (SBL2 == null))
|
|
throw new WPinternalsException("Bad GPT");
|
|
|
|
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 XmlSerializer(typeof(GPT), "");
|
|
MemoryStream s = new MemoryStream(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.Where(e => ((string.Compare(e.Name, NewPartition.Name, true) == 0) || (e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
|
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 + "\"");
|
|
}
|
|
}
|
|
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 + "\"");
|
|
if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector))
|
|
throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\"");
|
|
|
|
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 DecompressedStream(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 + "\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 + "\"");
|
|
}
|
|
}
|
|
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 + "\"");
|
|
if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector))
|
|
throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\"");
|
|
|
|
NewPartition.FirstSector = OldPartition.FirstSector;
|
|
NewPartition.LastSector = OldPartition.LastSector;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Partition> DynamicPartitions = new List<Partition>();
|
|
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<Partition> 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.Where(e => ((string.Compare(e.Name, OldPartition.Name, true) == 0) || (e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
|
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.Compare(n, NewPartition.Name, true) == 0))
|
|
continue;
|
|
|
|
// Sanity check
|
|
if (NewPartition.FirstSector < LowestSector)
|
|
throw new WPinternalsException("Bad sector alignment for partition: " + NewPartition.Name);
|
|
|
|
Partition CurrentPartition = this.GetPartition(NewPartition.Name);
|
|
if (CurrentPartition == null)
|
|
{
|
|
CurrentPartition = new Partition();
|
|
CurrentPartition.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 == null) || (NewPartition.PartitionGuid != CurrentPartition.PartitionGuid))
|
|
HasChanged = true;
|
|
if (NewPartition.PartitionGuid != null)
|
|
CurrentPartition.PartitionGuid = NewPartition.PartitionGuid;
|
|
if (CurrentPartition.PartitionGuid == null)
|
|
CurrentPartition.PartitionGuid = Guid.NewGuid();
|
|
|
|
if ((NewPartition.PartitionTypeGuid == null) || (NewPartition.PartitionTypeGuid != CurrentPartition.PartitionTypeGuid))
|
|
HasChanged = true;
|
|
if (NewPartition.PartitionTypeGuid != null)
|
|
CurrentPartition.PartitionTypeGuid = NewPartition.PartitionTypeGuid;
|
|
if (CurrentPartition.PartitionTypeGuid == null)
|
|
CurrentPartition.PartitionTypeGuid = Guid.NewGuid();
|
|
|
|
for (int i = this.Partitions.Count - 1; i >= 0; i--)
|
|
{
|
|
if (this.Partitions[i] != CurrentPartition)
|
|
{
|
|
if ((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.Where(e => ((string.Compare(e.Name, OldPartition.Name, true) == 0) || (e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
|
if (Entry != null)
|
|
{
|
|
DecompressedStream DecompressedStream = new DecompressedStream(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 + "\"");
|
|
}
|
|
|
|
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.Where(e => ((string.Compare(e.Name, NewPartition.Name, true) == 0) || (e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
|
DecompressedStream DecompressedStream = new DecompressedStream(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 (!Directory.Exists(DirPath))
|
|
Directory.CreateDirectory(DirPath);
|
|
|
|
XmlSerializer x = new XmlSerializer(typeof(GPT), "");
|
|
|
|
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
|
|
ns.Add("", "");
|
|
System.IO.StreamWriter FileWriter = new System.IO.StreamWriter(Path);
|
|
x.Serialize(FileWriter, this, ns);
|
|
FileWriter.Close();
|
|
}
|
|
|
|
internal static GPT ReadPartitions(string Path)
|
|
{
|
|
XmlSerializer x = new XmlSerializer(typeof(GPT), "");
|
|
using (FileStream s = new FileStream(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)
|
|
List<string> RevisePartitions = new List<string>(new string[] { "SBL1", "SBL2", "SBL3", "UEFI", "TZ", "RPM", "WINSECAPP" });
|
|
foreach (string RevisePartitionName in RevisePartitions)
|
|
{
|
|
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!");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|