Skip to content

Commit

Permalink
Move FlashPart creation logic to FlashPartFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
gus33000 committed Sep 21, 2024
1 parent d86743b commit 10dfbe8
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 196 deletions.
206 changes: 206 additions & 0 deletions Img2Ffu.Library/Writer/FlashPartFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using Img2Ffu.Writer.Streams;
using static Img2Ffu.GPT;

namespace Img2Ffu.Writer
{
internal static class FlashPartFactory
{
private static readonly string IS_UNLOCKED_PARTITION_NAME = "IS_UNLOCKED";
private static readonly string HACK_PARTITION_NAME = "HACK";
private static readonly string BACKUP_BS_NV_PARTITION_NAME = "BACKUP_BS_NV";
private static readonly string UEFI_BS_NV_PARTITION_NAME = "UEFI_BS_NV";

private static (ulong StartOffset, ulong Length)[] GetBlockAlignedAllocationMap((ulong StartOffset, ulong Length)[] AllocationMap, ulong TotalSizeInBytes, ulong BlockSize)
{
List<(ulong StartOffset, ulong Length)> AllocatedBlocks = [];

(ulong MaxStartOffset, ulong MaxLength) = AllocationMap.MaxBy(x => x.StartOffset + x.Length);
ulong MaxEndOffset = MaxStartOffset + MaxLength;

ulong MaxNumberOfBlocks = (MaxEndOffset / BlockSize) + 1;

(ulong MinStartOffset, ulong MinLength) = AllocationMap.MinBy(x => x.StartOffset);
ulong MinNumberOfBlocks = MinStartOffset / BlockSize;

for (ulong CurrentBlockIndex = MinNumberOfBlocks; CurrentBlockIndex < MaxNumberOfBlocks; CurrentBlockIndex++)
{
ulong CurrentBlockStartOffset = CurrentBlockIndex * BlockSize;
ulong CurrentBlockEndOffset = CurrentBlockStartOffset + BlockSize;

if (AllocationMap.Any(Allocation => CurrentBlockEndOffset > Allocation.StartOffset && Allocation.StartOffset + Allocation.Length > CurrentBlockStartOffset))
{
if (AllocatedBlocks.Count == 0)
{
AllocatedBlocks.Add((CurrentBlockStartOffset, BlockSize));
}
else
{
(ulong LastStartOffset, ulong LastLength) = AllocatedBlocks[^1];
if (LastStartOffset + LastLength == CurrentBlockStartOffset)
{
AllocatedBlocks[^1] = (LastStartOffset, LastLength + BlockSize);
}
else
{
AllocatedBlocks.Add((CurrentBlockStartOffset, BlockSize));
}
}
}
}

ulong NewAllocationTotalSizeInBytes = AllocatedBlocks[^1].StartOffset + AllocatedBlocks[^1].Length;

if (NewAllocationTotalSizeInBytes > TotalSizeInBytes)
{
AllocatedBlocks[^1] = (AllocatedBlocks[^1].StartOffset, AllocatedBlocks[^1].Length - (NewAllocationTotalSizeInBytes - TotalSizeInBytes));
}

return [.. AllocatedBlocks];
}

internal static List<FlashPart> GetFlashParts(GPT GPT, Stream stream, uint BlockSize, string[] ExcludedPartitionNames, uint sectorSize, ILogging Logging)
{
uint sectorsInABlock = BlockSize / sectorSize;

Logging.Log($"Sector Size: {sectorSize}");
Logging.Log($"Block Size: {BlockSize}");
Logging.Log($"Sectors in a Block: {sectorsInABlock}");

List<Partition> Partitions = GPT.Partitions;

bool isUnlocked = GPT.GetPartition(IS_UNLOCKED_PARTITION_NAME) != null;
bool isUnlockedSpecA = GPT.GetPartition(HACK_PARTITION_NAME) != null && GPT.GetPartition(BACKUP_BS_NV_PARTITION_NAME) != null;

if (isUnlocked)
{
Logging.Log($"The phone is an unlocked Spec B phone, {UEFI_BS_NV_PARTITION_NAME} will be kept in the FFU image for the unlock to work");
}

if (isUnlockedSpecA)
{
Logging.Log($"The phone is an UEFI unlocked Spec A phone, {UEFI_BS_NV_PARTITION_NAME} will be kept in the FFU image for the unlock to work");
}

List<FlashPart> flashParts = [];

Logging.Log("Partitions with a * appended are ignored partitions");
Logging.Log("");

int maxPartitionNameSize = Partitions.Select(x => x.Name.Length).Max() + 1;
int maxPartitionLastSector = Partitions.Select(x => x.LastSector.ToString().Length).Max() + 1;

Logging.Log($"{"Name".PadRight(maxPartitionNameSize)} - " +
$"{"First".PadRight(maxPartitionLastSector)} - " +
$"{"Last".PadRight(maxPartitionLastSector)} - " +
$"{"Sectors".PadRight(maxPartitionLastSector)} - " +
$"{"Blocks".PadRight(maxPartitionLastSector)}",
ILoggingLevel.Information);
Logging.Log("");

ulong CurrentStartingOffset = 0;
ulong CurrentEndingOffset = 0;
List<(ulong StartOffset, ulong Length)> AllocatedPartitionsMap = [];

foreach (Partition Partition in Partitions.OrderBy(x => x.FirstSector))
{
bool IsPartitionExcluded = false;

if (ExcludedPartitionNames.Any(x => x == Partition.Name))
{
IsPartitionExcluded = true;
if (isUnlocked && Partition.Name == UEFI_BS_NV_PARTITION_NAME)
{
IsPartitionExcluded = false;
}

if (isUnlockedSpecA && Partition.Name == UEFI_BS_NV_PARTITION_NAME)
{
IsPartitionExcluded = false;
}
}

string name = $"{(IsPartitionExcluded ? "*" : "")}{Partition.Name}";

Logging.Log($"{name.PadRight(maxPartitionNameSize)} - " +
$"{(Partition.FirstSector + "s").PadRight(maxPartitionLastSector)} - " +
$"{(Partition.LastSector + "s").PadRight(maxPartitionLastSector)} - " +
$"{(Partition.SizeInSectors + "s").PadRight(maxPartitionLastSector)} - " +
$"{((Partition.SizeInSectors / (double)sectorsInABlock) + "c").PadRight(maxPartitionLastSector)}",
IsPartitionExcluded ? ILoggingLevel.Warning : ILoggingLevel.Information);

ulong CurrentPartitionStartingOffset = Partition.FirstSector * sectorSize;
ulong CurrentPartitionEndingOffset = (Partition.LastSector + 1) * sectorSize;

if (IsPartitionExcluded)
{
if (AllocatedPartitionsMap.Count != 0)
{
ulong AllocationTotalSizeInBytes = CurrentEndingOffset - CurrentStartingOffset;
(ulong StartOffset, ulong Length)[] AllocatedBlocks = GetBlockAlignedAllocationMap([.. AllocatedPartitionsMap], AllocationTotalSizeInBytes, BlockSize);

foreach ((ulong StartOffset, ulong Length) in AllocatedBlocks)
{
ulong blockStartOffset = CurrentStartingOffset + StartOffset;
PartialStream blockStream = new(stream, (long)blockStartOffset, (long)(blockStartOffset + Length));
FlashPart flashPart = new(blockStream, blockStartOffset);
flashParts.Add(flashPart);
}

AllocatedPartitionsMap.Clear();
CurrentStartingOffset = 0;
CurrentEndingOffset = 0;
}
}
else
{
// 0 would be the GPT, we can't land in this case here because we deal with partitions
if (CurrentStartingOffset == 0)
{
CurrentStartingOffset = CurrentPartitionStartingOffset;
CurrentEndingOffset = CurrentPartitionEndingOffset;
}
else
{
CurrentEndingOffset = CurrentPartitionEndingOffset;
}

ulong allocationOffset = CurrentPartitionStartingOffset - CurrentStartingOffset;

PartialStream partialStream = new(stream, (long)CurrentPartitionStartingOffset, (long)CurrentPartitionEndingOffset);

/*if (FileSystemAllocationUtils.IsNTFS(partialStream))
{
(ulong StartOffset, ulong Length)[] AllocatedClusterMap = FileSystemAllocationUtils.GetNTFSAllocatedClustersMap(partialStream);
//AllocatedPartitionsMap.AddRange(AllocatedClusterMap.Select(x => (allocationOffset + x.StartOffset, x.Length)));
(ulong StartOffset, ulong Length) lastElement = AllocatedClusterMap.MaxBy(x => x.StartOffset);
AllocatedPartitionsMap.Add((allocationOffset, lastElement.StartOffset + lastElement.Length));
}
else*/
{
AllocatedPartitionsMap.Add((allocationOffset, Partition.SizeInSectors * sectorSize));
}
}
}

if (AllocatedPartitionsMap.Count != 0)
{
(ulong StartOffset, ulong Length)[] AllocatedBlocks = GetBlockAlignedAllocationMap([.. AllocatedPartitionsMap], CurrentStartingOffset - CurrentEndingOffset, BlockSize);

foreach ((ulong StartOffset, ulong Length) in AllocatedBlocks)
{
ulong blockStartOffset = CurrentStartingOffset + StartOffset;
PartialStream blockStream = new(stream, (long)blockStartOffset, (long)(blockStartOffset + Length));
FlashPart flashPart = new(blockStream, blockStartOffset);
flashParts.Add(flashPart);
}

AllocatedPartitionsMap.Clear();
CurrentStartingOffset = 0;
CurrentEndingOffset = 0;
}

return flashParts;
}
}
}
Loading

0 comments on commit 10dfbe8

Please sign in to comment.