## @file
# This file is used to the implementation of Bios layout handler.
#
# Copyright (c) 2021-, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
import os
from core.BiosTree import *
from core.GuidTools import GUIDTools
from core.BiosTreeNode import *
from FirmwareStorageFormat.Common import *
from utils.FmmtLogger import FmmtLogger as logger

EFI_FVB2_ERASE_POLARITY = 0x00000800

def ChangeSize(TargetTree, size_delta: int=0) -> None:
    # If Size increase delta, then should be: size_delta = -delta
    if type(TargetTree.Data.Header) == type(EFI_FFS_FILE_HEADER2()) or type(TargetTree.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()):
        TargetTree.Data.Size -= size_delta
        TargetTree.Data.Header.ExtendedSize -= size_delta
    elif TargetTree.type == SECTION_TREE and TargetTree.Data.OriData:
        OriSize = TargetTree.Data.Header.SECTION_SIZE
        OriSize -= size_delta
        TargetTree.Data.Header.Size[0] = OriSize % (16**2)
        TargetTree.Data.Header.Size[1] = OriSize % (16**4) //(16**2)
        TargetTree.Data.Header.Size[2] = OriSize // (16**4)
    else:
        TargetTree.Data.Size -= size_delta
        TargetTree.Data.Header.Size[0] = TargetTree.Data.Size % (16**2)
        TargetTree.Data.Header.Size[1] = TargetTree.Data.Size % (16**4) //(16**2)
        TargetTree.Data.Header.Size[2] = TargetTree.Data.Size // (16**4)

def ModifyFfsType(TargetFfs) -> None:
    if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER()) and TargetFfs.Data.Size > 0xFFFFFF:
        ExtendSize = TargetFfs.Data.Header.FFS_FILE_SIZE + 8
        New_Header = EFI_FFS_FILE_HEADER2()
        New_Header.Name = TargetFfs.Data.Header.Name
        New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck
        New_Header.Type = TargetFfs.Data.Header.Type
        New_Header.Attributes = TargetFfs.Data.Header.Attributes | 0x01  # set the Attribute with FFS_ATTRIB_LARGE_FILE (0x01)
        NewSize = 0
        New_Header.Size[0] = NewSize % (16**2)    # minus the delta size of Header
        New_Header.Size[1] = NewSize % (16**4) //(16**2)
        New_Header.Size[2] = NewSize // (16**4)
        New_Header.State = TargetFfs.Data.Header.State
        New_Header.ExtendedSize = ExtendSize
        TargetFfs.Data.Header = New_Header
        TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE
        TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength
        TargetFfs.Data.ModCheckSum()
    elif type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()) and TargetFfs.Data.Size <= 0xFFFFFF:
        New_Header = EFI_FFS_FILE_HEADER()
        New_Header.Name = TargetFfs.Data.Header.Name
        New_Header.IntegrityCheck = TargetFfs.Data.Header.IntegrityCheck
        New_Header.Type = TargetFfs.Data.Header.Type
        New_Header.Attributes = TargetFfs.Data.Header.Attributes - 1  # remove the FFS_ATTRIB_LARGE_FILE (0x01) from Attribute
        New_Header.Size[0] = (TargetFfs.Data.Size - 8) % (16**2)    # minus the delta size of Header
        New_Header.Size[1] = (TargetFfs.Data.Size - 8) % (16**4) //(16**2)
        New_Header.Size[2] = (TargetFfs.Data.Size - 8) // (16**4)
        New_Header.State = TargetFfs.Data.Header.State
        TargetFfs.Data.Header = New_Header
        TargetFfs.Data.Size = TargetFfs.Data.Header.FFS_FILE_SIZE
        TargetFfs.Data.HeaderLength = TargetFfs.Data.Header.HeaderLength
        TargetFfs.Data.ModCheckSum()
        if struct2stream(TargetFfs.Parent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM3_GUID_BYTE:
            NeedChange = True
            for item in TargetFfs.Parent.Child:
                if type(item.Data.Header) == type(EFI_FFS_FILE_HEADER2()):
                    NeedChange = False
            if NeedChange:
                TargetFfs.Parent.Data.Header.FileSystemGuid = ModifyGuidFormat("8c8ce578-8a3d-4f1c-9935-896185c32dd3")

    if type(TargetFfs.Data.Header) == type(EFI_FFS_FILE_HEADER2()):
        TarParent = TargetFfs.Parent
        while TarParent:
            if TarParent.type == FV_TREE and struct2stream(TarParent.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:
                TarParent.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")
            TarParent = TarParent.Parent

def PadSectionModify(PadSection, Offset) -> None:
    # Offset > 0, Size decrease; Offset < 0, Size increase;
    ChangeSize(PadSection, Offset)
    PadSection.Data.Data = (PadSection.Data.Size - PadSection.Data.HeaderLength) * b'\xff'

def ModifySectionType(TargetSection) -> None:
    # If Section Size is increased larger than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER to EFI_COMMON_SECTION_HEADER2.
    if type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER()) and TargetSection.Data.Size >= 0xFFFFFF:
        New_Header = EFI_COMMON_SECTION_HEADER2()
        New_Header.Type = TargetSection.Data.Header.Type
        NewSize = 0xFFFFFF
        New_Header.Size[0] = NewSize % (16**2)    # minus the delta size of Header
        New_Header.Size[1] = NewSize % (16**4) //(16**2)
        New_Header.Size[2] = NewSize // (16**4)
        New_Header.ExtendedSize = TargetSection.Data.Size + 4
        TargetSection.Data.Header = New_Header
        TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE
        # Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.
        if TargetSection.LastRel.Data.IsPadSection:
            PadSectionModify(TargetSection.LastRel, -4)
        else:
            SecParent = TargetSection.Parent
            Target_index = SecParent.Child.index(TargetSection)
            NewPadSection = SectionNode(b'\x00\x00\x00\x19')
            SecParent.insertChild(NewPadSection, Target_index)
    # If Section Size is decreased smaller than 0xFFFFFF, need modify Section Header from EFI_COMMON_SECTION_HEADER2 to EFI_COMMON_SECTION_HEADER.
    elif type(TargetSection.Data.Header) == type(EFI_COMMON_SECTION_HEADER2()) and TargetSection.Data.Size < 0xFFFFFF:
        New_Header = EFI_COMMON_SECTION_HEADER()
        New_Header.Type = TargetSection.Data.Header.Type
        New_Header.Size[0] = (TargetSection.Data.Size - 4) % (16**2)    # minus the delta size of Header
        New_Header.Size[1] = (TargetSection.Data.Size - 4) % (16**4) //(16**2)
        New_Header.Size[2] = (TargetSection.Data.Size - 4) // (16**4)
        TargetSection.Data.Header = New_Header
        TargetSection.Data.Size = TargetSection.Data.Header.SECTION_SIZE
        # Align the Header's added 4 bit to 8-alignment to promise the following Ffs's align correctly.
        if TargetSection.LastRel.Data.IsPadSection:
            PadSectionModify(TargetSection.LastRel, -4)
        else:
            SecParent = TargetSection.Parent
            Target_index = SecParent.Child.index(TargetSection)
            NewPadSection = SectionNode(b'\x00\x00\x00\x19')
            SecParent.insertChild(NewPadSection, Target_index)

def ModifyFvExtData(TreeNode) -> None:
    FvExtData = b''
    if TreeNode.Data.Header.ExtHeaderOffset:
        FvExtHeader = struct2stream(TreeNode.Data.ExtHeader)
        FvExtData += FvExtHeader
    if TreeNode.Data.Header.ExtHeaderOffset and TreeNode.Data.ExtEntryExist:
        FvExtEntry = struct2stream(TreeNode.Data.ExtEntry)
        FvExtData += FvExtEntry
    if FvExtData:
        InfoNode = TreeNode.Child[0]
        InfoNode.Data.Data = FvExtData + InfoNode.Data.Data[TreeNode.Data.ExtHeader.ExtHeaderSize:]
        InfoNode.Data.ModCheckSum()

def ModifyFvSystemGuid(TargetFv) -> None:
    if struct2stream(TargetFv.Data.Header.FileSystemGuid) == EFI_FIRMWARE_FILE_SYSTEM2_GUID_BYTE:
        TargetFv.Data.Header.FileSystemGuid = ModifyGuidFormat("5473C07A-3DCB-4dca-BD6F-1E9689E7349A")
    TargetFv.Data.ModCheckSum()
    TargetFv.Data.Data = b''
    for item in TargetFv.Child:
        if item.type == FFS_FREE_SPACE:
            TargetFv.Data.Data += item.Data.Data + item.Data.PadData
        else:
            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData

class FvHandler:
    def __init__(self, NewFfs, TargetFfs=None) -> None:
        self.NewFfs = NewFfs
        self.TargetFfs = TargetFfs
        self.Status = False
        self.Remain_New_Free_Space = 0

    ## Use for Compress the Section Data
    def CompressData(self, TargetTree) -> None:
        TreePath = TargetTree.GetTreePath()
        pos = len(TreePath)
        while pos:
            if not self.Status:
                if TreePath[pos-1].type == SECTION_TREE and TreePath[pos-1].Data.Type == 0x02:
                    self.CompressSectionData(TreePath[pos-1], None, TreePath[pos-1].Data.ExtHeader.SectionDefinitionGuid)
                else:
                    if pos == len(TreePath):
                        self.CompressSectionData(TreePath[pos-1], pos)
                    else:
                        self.CompressSectionData(TreePath[pos-1], None)
            pos -= 1

    def CompressSectionData(self, TargetTree, pos: int, GuidTool=None) -> None:
        NewData = b''
        temp_save_child = TargetTree.Child
        if TargetTree.Data:
            # Update current node data as adding all the header and data of its child node.
            for item in temp_save_child:
                if item.type == SECTION_TREE and not item.Data.OriData and item.Data.ExtHeader:
                    NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
                elif item.type == SECTION_TREE and item.Data.OriData and not item.Data.ExtHeader:
                    NewData += struct2stream(item.Data.Header) + item.Data.OriData + item.Data.PadData
                elif item.type == SECTION_TREE and item.Data.OriData and item.Data.ExtHeader:
                    NewData += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
                elif item.type == FFS_FREE_SPACE:
                    NewData += item.Data.Data + item.Data.PadData
                else:
                    NewData += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData
            # If node is FFS_TREE, update Pad data and Header info.
            # Remain_New_Free_Space is used for move more free space into lst level Fv.
            if TargetTree.type == FFS_TREE:
                New_Pad_Size = GetPadSize(len(NewData), 8)
                Size_delta = len(NewData) - len(TargetTree.Data.Data)
                ChangeSize(TargetTree, -Size_delta)
                Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size
                self.Remain_New_Free_Space += Delta_Pad_Size
                TargetTree.Data.PadData = b'\xff' * New_Pad_Size
                TargetTree.Data.ModCheckSum()
            # If node is FV_TREE, update Pad data and Header info.
            # Consume Remain_New_Free_Space is used for move more free space into lst level Fv.
            elif TargetTree.type == FV_TREE or TargetTree.type == SEC_FV_TREE and not pos:
                if self.Remain_New_Free_Space:
                    if TargetTree.Data.Free_Space:
                        TargetTree.Data.Free_Space += self.Remain_New_Free_Space
                        NewData += self.Remain_New_Free_Space * b'\xff'
                        TargetTree.Child[-1].Data.Data += self.Remain_New_Free_Space * b'\xff'
                    else:
                        TargetTree.Data.Data += self.Remain_New_Free_Space * b'\xff'
                        New_Free_Space = BIOSTREE('FREE_SPACE')
                        New_Free_Space.type = FFS_FREE_SPACE
                        New_Free_Space.Data = FreeSpaceNode(b'\xff' * self.Remain_New_Free_Space)
                        TargetTree.insertChild(New_Free_Space)
                    self.Remain_New_Free_Space = 0
                if TargetTree.type == SEC_FV_TREE:
                    Size_delta = len(NewData) + self.Remain_New_Free_Space - len(TargetTree.Data.Data)
                    TargetTree.Data.Header.FvLength += Size_delta
                TargetTree.Data.ModFvExt()
                TargetTree.Data.ModFvSize()
                TargetTree.Data.ModExtHeaderData()
                ModifyFvExtData(TargetTree)
                TargetTree.Data.ModCheckSum()
            # If node is SECTION_TREE and not guided section, update Pad data and Header info.
            # Remain_New_Free_Space is used for move more free space into lst level Fv.
            elif TargetTree.type == SECTION_TREE and TargetTree.Data.Type != 0x02:
                New_Pad_Size = GetPadSize(len(NewData), 4)
                Size_delta = len(NewData) - len(TargetTree.Data.Data)
                ChangeSize(TargetTree, -Size_delta)
                if TargetTree.NextRel:
                    Delta_Pad_Size = len(TargetTree.Data.PadData) - New_Pad_Size
                    self.Remain_New_Free_Space += Delta_Pad_Size
                    TargetTree.Data.PadData = b'\x00' * New_Pad_Size
            TargetTree.Data.Data = NewData
        if GuidTool:
            guidtool = GUIDTools().__getitem__(struct2stream(GuidTool))
            if not guidtool.ifexist:
                logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, TargetTree.Parent.Data.Name))
                raise Exception("Process Failed: GuidTool not found!")
            CompressedData = guidtool.pack(TargetTree.Data.Data)
            if len(CompressedData) < len(TargetTree.Data.OriData):
                New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)
                Size_delta = len(CompressedData) - len(TargetTree.Data.OriData)
                ChangeSize(TargetTree, -Size_delta)
                if TargetTree.NextRel:
                    TargetTree.Data.PadData = b'\x00' * New_Pad_Size
                    self.Remain_New_Free_Space = len(TargetTree.Data.OriData) + len(TargetTree.Data.PadData) - len(CompressedData) - New_Pad_Size
                else:
                    TargetTree.Data.PadData = b''
                    self.Remain_New_Free_Space = len(TargetTree.Data.OriData) - len(CompressedData)
                TargetTree.Data.OriData = CompressedData
            elif len(CompressedData) == len(TargetTree.Data.OriData):
                TargetTree.Data.OriData = CompressedData
            elif len(CompressedData) > len(TargetTree.Data.OriData):
                New_Pad_Size = GetPadSize(len(CompressedData), SECTION_COMMON_ALIGNMENT)
                self.Remain_New_Free_Space = len(CompressedData) + New_Pad_Size - len(TargetTree.Data.OriData) - len(TargetTree.Data.PadData)
                self.ModifyTest(TargetTree, self.Remain_New_Free_Space)
                self.Status = True

    def ModifyTest(self, ParTree, Needed_Space: int) -> None:
        # If have needed space, will find if there have free space in parent tree, meanwhile update the node data.
        if Needed_Space > 0:
            # If current node is a Fv node
            if ParTree.type == FV_TREE or ParTree.type == SEC_FV_TREE:
                ParTree.Data.Data = b''
                # First check if Fv free space is enough for needed space.
                # If so, use the current Fv free space;
                # Else, use all the Free space, and recalculate needed space, continue finding in its parent node.
                Needed_Space = Needed_Space - ParTree.Data.Free_Space
                if Needed_Space < 0:
                    ParTree.Child[-1].Data.Data = b'\xff' * (-Needed_Space)
                    ParTree.Data.Free_Space = (-Needed_Space)
                    self.Status = True
                else:
                    if ParTree.type == FV_TREE:
                        self.Status = False
                    else:
                        BlockSize = ParTree.Data.Header.BlockMap[0].Length
                        New_Add_Len = BlockSize - Needed_Space%BlockSize
                        if New_Add_Len % BlockSize:
                            ParTree.Child[-1].Data.Data = b'\xff' * New_Add_Len
                            ParTree.Data.Free_Space = New_Add_Len
                            Needed_Space += New_Add_Len
                        else:
                            ParTree.Child.remove(ParTree.Child[-1])
                            ParTree.Data.Free_Space = 0
                        ParTree.Data.Size += Needed_Space
                        ParTree.Data.Header.Fvlength = ParTree.Data.Size
                ModifyFvSystemGuid(ParTree)
                for item in ParTree.Child:
                    if item.type == FFS_FREE_SPACE:
                        ParTree.Data.Data += item.Data.Data + item.Data.PadData
                    else:
                        ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
                ParTree.Data.ModFvExt()
                ParTree.Data.ModFvSize()
                ParTree.Data.ModExtHeaderData()
                ModifyFvExtData(ParTree)
                ParTree.Data.ModCheckSum()
            # If current node is a Ffs node
            elif ParTree.type == FFS_TREE:
                ParTree.Data.Data = b''
                OriHeaderLen = ParTree.Data.HeaderLength
                # Update its data as adding all the header and data of its child node.
                for item in ParTree.Child:
                    if item.Data.OriData:
                        if item.Data.ExtHeader:
                            ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
                        else:
                            ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.OriData + item.Data.PadData
                    else:
                        if item.Data.ExtHeader:
                            ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
                        else:
                            ParTree.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
                ChangeSize(ParTree, -Needed_Space)
                ModifyFfsType(ParTree)
                # Recalculate pad data, update needed space with Delta_Pad_Size.
                Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
                New_Pad_Size = GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)
                Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
                Needed_Space += Delta_Pad_Size
                ParTree.Data.PadData = b'\xff' * GetPadSize(ParTree.Data.Size, FFS_COMMON_ALIGNMENT)
                ParTree.Data.ModCheckSum()
            # If current node is a Section node
            elif ParTree.type == SECTION_TREE:
                OriData = ParTree.Data.Data
                OriHeaderLen = ParTree.Data.HeaderLength
                ParTree.Data.Data = b''
                # Update its data as adding all the header and data of its child node.
                for item in ParTree.Child:
                    if item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type != 0x02:
                        ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.Data + item.Data.PadData
                    elif item.type == SECTION_TREE and item.Data.ExtHeader and item.Data.Type == 0x02:
                        ParTree.Data.Data += struct2stream(item.Data.Header) + struct2stream(item.Data.ExtHeader) + item.Data.OriData + item.Data.PadData
                    else:
                        ParTree.Data.Data += struct2stream(item.Data.Header) + item.Data.Data + item.Data.PadData
                # If the current section is guided section
                if ParTree.Data.Type == 0x02:
                    guidtool = GUIDTools().__getitem__(struct2stream(ParTree.Data.ExtHeader.SectionDefinitionGuid))
                    if not guidtool.ifexist:
                        logger.error("GuidTool {} is not found when decompressing {} file.\n".format(guidtool.command, ParTree.Parent.Data.Name))
                        raise Exception("Process Failed: GuidTool not found!")
                    # Recompress current data, and recalculate the needed space
                    CompressedData = guidtool.pack(ParTree.Data.Data)
                    Needed_Space = len(CompressedData) - len(ParTree.Data.OriData)
                    ParTree.Data.OriData = CompressedData
                    New_Size = ParTree.Data.HeaderLength + len(CompressedData)
                    ParTree.Data.Header.Size[0] = New_Size % (16**2)
                    ParTree.Data.Header.Size[1] = New_Size % (16**4) //(16**2)
                    ParTree.Data.Header.Size[2] = New_Size // (16**4)
                    ParTree.Data.Size = ParTree.Data.Header.SECTION_SIZE
                    ModifySectionType(ParTree)
                    Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
                    # Update needed space with Delta_Pad_Size
                    if ParTree.NextRel:
                        New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)
                        Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
                        ParTree.Data.PadData = b'\x00' * New_Pad_Size
                        Needed_Space += Delta_Pad_Size
                    else:
                        ParTree.Data.PadData = b''
                    if Needed_Space < 0:
                        self.Remain_New_Free_Space = len(ParTree.Data.OriData) - len(CompressedData)
                # If current section is not guided section
                elif Needed_Space:
                    ChangeSize(ParTree, -Needed_Space)
                    ModifySectionType(ParTree)
                    # Update needed space with Delta_Pad_Size
                    Needed_Space += ParTree.Data.HeaderLength - OriHeaderLen
                    New_Pad_Size = GetPadSize(ParTree.Data.Size, SECTION_COMMON_ALIGNMENT)
                    Delta_Pad_Size = New_Pad_Size - len(ParTree.Data.PadData)
                    Needed_Space += Delta_Pad_Size
                    ParTree.Data.PadData = b'\x00' * New_Pad_Size
            NewParTree = ParTree.Parent
            ROOT_TYPE = [ROOT_FV_TREE, ROOT_FFS_TREE, ROOT_SECTION_TREE, ROOT_TREE]
            if NewParTree and NewParTree.type not in ROOT_TYPE:
                self.ModifyTest(NewParTree, Needed_Space)
        # If current node have enough space, will recompress all the related node data, return true.
        else:
            self.CompressData(ParTree)
            self.Status = True

    def ReplaceFfs(self) -> bool:
        logger.debug('Start Replacing Process......')
        TargetFv = self.TargetFfs.Parent
        # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.
        if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
                self.NewFfs.Data.Header.State = c_uint8(
                    ~self.NewFfs.Data.Header.State)
        # NewFfs parsing will not calculate the PadSize, thus recalculate.
        self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)
        if self.NewFfs.Data.Size >= self.TargetFfs.Data.Size:
            Needed_Space = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)
            # If TargetFv have enough free space, just move part of the free space to NewFfs.
            if TargetFv.Data.Free_Space >= Needed_Space:
                # Modify TargetFv Child info and BiosTree.
                TargetFv.Child[-1].Data.Data = b'\xff' * (TargetFv.Data.Free_Space - Needed_Space)
                TargetFv.Data.Free_Space -= Needed_Space
                Target_index = TargetFv.Child.index(self.TargetFfs)
                TargetFv.Child.remove(self.TargetFfs)
                TargetFv.insertChild(self.NewFfs, Target_index)
                # Modify TargetFv Header and ExtHeader info.
                TargetFv.Data.ModFvExt()
                TargetFv.Data.ModFvSize()
                TargetFv.Data.ModExtHeaderData()
                ModifyFvExtData(TargetFv)
                TargetFv.Data.ModCheckSum()
                # Recompress from the Fv node to update all the related node data.
                self.CompressData(TargetFv)
                # return the Status
                self.Status = True
            # If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
            else:
                if TargetFv.type == FV_TREE:
                    self.Status = False
                else:
                    # Recalculate TargetFv needed space to keep it match the BlockSize setting.
                    Needed_Space -= TargetFv.Data.Free_Space
                    BlockSize = TargetFv.Data.Header.BlockMap[0].Length
                    New_Add_Len = BlockSize - Needed_Space%BlockSize
                    Target_index = TargetFv.Child.index(self.TargetFfs)
                    if New_Add_Len % BlockSize:
                        TargetFv.Child[-1].Data.Data = b'\xff' * New_Add_Len
                        TargetFv.Data.Free_Space = New_Add_Len
                        Needed_Space += New_Add_Len
                        TargetFv.insertChild(self.NewFfs, Target_index)
                        TargetFv.Child.remove(self.TargetFfs)
                    else:
                        TargetFv.Child.remove(self.TargetFfs)
                        TargetFv.Data.Free_Space = 0
                        TargetFv.insertChild(self.NewFfs)
                    # Encapsulate the Fv Data for update.
                    TargetFv.Data.Data = b''
                    for item in TargetFv.Child:
                        if item.type == FFS_FREE_SPACE:
                            TargetFv.Data.Data += item.Data.Data + item.Data.PadData
                        else:
                            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
                    TargetFv.Data.Size += Needed_Space
                    # Modify TargetFv Data Header and ExtHeader info.
                    TargetFv.Data.Header.FvLength = TargetFv.Data.Size
                    TargetFv.Data.ModFvExt()
                    TargetFv.Data.ModFvSize()
                    TargetFv.Data.ModExtHeaderData()
                    ModifyFvExtData(TargetFv)
                    TargetFv.Data.ModCheckSum()
                    # Start free space calculating and moving process.
                    self.ModifyTest(TargetFv.Parent, Needed_Space)
        else:
            New_Free_Space = self.TargetFfs.Data.Size - self.NewFfs.Data.Size
            # If TargetFv already have free space, move the new free space into it.
            if TargetFv.Data.Free_Space:
                TargetFv.Child[-1].Data.Data += b'\xff' * New_Free_Space
                TargetFv.Data.Free_Space += New_Free_Space
                Target_index = TargetFv.Child.index(self.TargetFfs)
                TargetFv.Child.remove(self.TargetFfs)
                TargetFv.insertChild(self.NewFfs, Target_index)
                self.Status = True
            # If TargetFv do not have free space, create free space for Fv.
            else:
                New_Free_Space_Tree = BIOSTREE('FREE_SPACE')
                New_Free_Space_Tree.type = FFS_FREE_SPACE
                New_Free_Space_Tree.Data = FfsNode(b'\xff' * New_Free_Space)
                TargetFv.Data.Free_Space = New_Free_Space
                TargetFv.insertChild(New_Free_Space)
                Target_index = TargetFv.Child.index(self.TargetFfs)
                TargetFv.Child.remove(self.TargetFfs)
                TargetFv.insertChild(self.NewFfs, Target_index)
                self.Status = True
            # Modify TargetFv Header and ExtHeader info.
            TargetFv.Data.ModFvExt()
            TargetFv.Data.ModFvSize()
            TargetFv.Data.ModExtHeaderData()
            ModifyFvExtData(TargetFv)
            TargetFv.Data.ModCheckSum()
            # Recompress from the Fv node to update all the related node data.
            self.CompressData(TargetFv)
        logger.debug('Done!')
        return self.Status

    def AddFfs(self) -> bool:
        logger.debug('Start Adding Process......')
        # NewFfs parsing will not calculate the PadSize, thus recalculate.
        self.NewFfs.Data.PadData = b'\xff' * GetPadSize(self.NewFfs.Data.Size, FFS_COMMON_ALIGNMENT)
        if self.TargetFfs.type == FFS_FREE_SPACE:
            TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData) - self.TargetFfs.Data.Size - len(self.TargetFfs.Data.PadData)
            TargetFv = self.TargetFfs.Parent
            # If the Fv Header Attributes is EFI_FVB2_ERASE_POLARITY, Child Ffs Header State need be reversed.
            if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
                self.NewFfs.Data.Header.State = c_uint8(
                    ~self.NewFfs.Data.Header.State)
            # If TargetFv have enough free space, just move part of the free space to NewFfs, split free space to NewFfs and new free space.
            if TargetLen < 0:
                self.TargetFfs.Data.Data = b'\xff' * (-TargetLen)
                TargetFv.Data.Free_Space = (-TargetLen)
                TargetFv.Data.ModFvExt()
                TargetFv.Data.ModExtHeaderData()
                ModifyFvExtData(TargetFv)
                TargetFv.Data.ModCheckSum()
                TargetFv.insertChild(self.NewFfs, -1)
                ModifyFfsType(self.NewFfs)
                # Recompress from the Fv node to update all the related node data.
                self.CompressData(TargetFv)
                self.Status = True
            elif TargetLen == 0:
                TargetFv.Child.remove(self.TargetFfs)
                TargetFv.insertChild(self.NewFfs)
                ModifyFfsType(self.NewFfs)
                # Recompress from the Fv node to update all the related node data.
                self.CompressData(TargetFv)
                self.Status = True
            # If TargetFv do not have enough free space, need move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
            else:
                if TargetFv.type == FV_TREE:
                    self.Status = False
                elif TargetFv.type == SEC_FV_TREE:
                    # Recalculate TargetFv needed space to keep it match the BlockSize setting.
                    BlockSize = TargetFv.Data.Header.BlockMap[0].Length
                    New_Add_Len = BlockSize - TargetLen%BlockSize
                    if New_Add_Len % BlockSize:
                        self.TargetFfs.Data.Data = b'\xff' * New_Add_Len
                        self.TargetFfs.Data.Size = New_Add_Len
                        TargetLen += New_Add_Len
                        TargetFv.insertChild(self.NewFfs, -1)
                        TargetFv.Data.Free_Space = New_Add_Len
                    else:
                        TargetFv.Child.remove(self.TargetFfs)
                        TargetFv.insertChild(self.NewFfs)
                        TargetFv.Data.Free_Space = 0
                    ModifyFfsType(self.NewFfs)
                    ModifyFvSystemGuid(TargetFv)
                    TargetFv.Data.Data = b''
                    for item in TargetFv.Child:
                        if item.type == FFS_FREE_SPACE:
                            TargetFv.Data.Data += item.Data.Data + item.Data.PadData
                        else:
                            TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
                    # Encapsulate the Fv Data for update.
                    TargetFv.Data.Size += TargetLen
                    TargetFv.Data.Header.FvLength = TargetFv.Data.Size
                    TargetFv.Data.ModFvExt()
                    TargetFv.Data.ModFvSize()
                    TargetFv.Data.ModExtHeaderData()
                    ModifyFvExtData(TargetFv)
                    TargetFv.Data.ModCheckSum()
                    # Start free space calculating and moving process.
                    self.ModifyTest(TargetFv.Parent, TargetLen)
        else:
            # If TargetFv do not have free space, need directly move part of the free space of TargetFv's parent Fv to TargetFv/NewFfs.
            TargetLen = self.NewFfs.Data.Size + len(self.NewFfs.Data.PadData)
            TargetFv = self.TargetFfs.Parent
            if TargetFv.Data.Header.Attributes & EFI_FVB2_ERASE_POLARITY:
                self.NewFfs.Data.Header.State = c_uint8(
                    ~self.NewFfs.Data.Header.State)
            if TargetFv.type == FV_TREE:
                self.Status = False
            elif TargetFv.type == SEC_FV_TREE:
                BlockSize = TargetFv.Data.Header.BlockMap[0].Length
                New_Add_Len = BlockSize - TargetLen%BlockSize
                if New_Add_Len % BlockSize:
                    New_Free_Space = BIOSTREE('FREE_SPACE')
                    New_Free_Space.type = FFS_FREE_SPACE
                    New_Free_Space.Data = FreeSpaceNode(b'\xff' * New_Add_Len)
                    TargetLen += New_Add_Len
                    TargetFv.Data.Free_Space = New_Add_Len
                    TargetFv.insertChild(self.NewFfs)
                    TargetFv.insertChild(New_Free_Space)
                else:
                    TargetFv.insertChild(self.NewFfs)
                ModifyFfsType(self.NewFfs)
                ModifyFvSystemGuid(TargetFv)
                TargetFv.Data.Data = b''
                for item in TargetFv.Child:
                    if item.type == FFS_FREE_SPACE:
                        TargetFv.Data.Data += item.Data.Data + item.Data.PadData
                    else:
                        TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
                TargetFv.Data.Size += TargetLen
                TargetFv.Data.Header.FvLength = TargetFv.Data.Size
                TargetFv.Data.ModFvExt()
                TargetFv.Data.ModFvSize()
                TargetFv.Data.ModExtHeaderData()
                ModifyFvExtData(TargetFv)
                TargetFv.Data.ModCheckSum()
                self.ModifyTest(TargetFv.Parent, TargetLen)
        logger.debug('Done!')
        return self.Status

    def DeleteFfs(self) -> bool:
        logger.debug('Start Deleting Process......')
        Delete_Ffs = self.TargetFfs
        Delete_Fv = Delete_Ffs.Parent
        # Calculate free space
        Add_Free_Space = Delete_Ffs.Data.Size + len(Delete_Ffs.Data.PadData)
        # If Ffs parent Fv have free space, follow the rules to merge the new free space.
        if Delete_Fv.Data.Free_Space:
            # If Fv is a Section fv, free space need to be recalculated to keep align with BlockSize.
            # Other free space saved in self.Remain_New_Free_Space, will be moved to the 1st level Fv.
            if Delete_Fv.type == SEC_FV_TREE:
                Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space
                BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length
                New_Free_Space = BlockSize - Used_Size % BlockSize
                self.Remain_New_Free_Space += Delete_Fv.Data.Free_Space + Add_Free_Space - New_Free_Space
                Delete_Fv.Child[-1].Data.Data = New_Free_Space * b'\xff'
                Delete_Fv.Data.Free_Space = New_Free_Space
            # If Fv is lst level Fv, new free space will be merged with origin free space.
            else:
                Used_Size = Delete_Fv.Data.Size - Delete_Fv.Data.Free_Space - Add_Free_Space
                Delete_Fv.Child[-1].Data.Data += Add_Free_Space * b'\xff'
                Delete_Fv.Data.Free_Space += Add_Free_Space
                New_Free_Space = Delete_Fv.Data.Free_Space
        # If Ffs parent Fv not have free space, will create new free space node to save the free space.
        else:
            # If Fv is a Section fv, new free space need to be recalculated to keep align with BlockSize.
            # Then create a Free spcae node to save the 0xff data, and insert into the Fv.
            # If have more space left, move to 1st level fv.
            if Delete_Fv.type == SEC_FV_TREE:
                Used_Size = Delete_Fv.Data.Size - Add_Free_Space
                BlockSize = Delete_Fv.Data.Header.BlockMap[0].Length
                New_Free_Space = BlockSize - Used_Size % BlockSize
                self.Remain_New_Free_Space += Add_Free_Space - New_Free_Space
                Add_Free_Space = New_Free_Space
            # If Fv is lst level Fv, new free space node will be created to save the free space.
            else:
                Used_Size = Delete_Fv.Data.Size - Add_Free_Space
                New_Free_Space = Add_Free_Space
            New_Free_Space_Info = FfsNode(Add_Free_Space * b'\xff')
            New_Free_Space_Info.Data = Add_Free_Space * b'\xff'
            New_Ffs_Tree = BIOSTREE(New_Free_Space_Info.Name)
            New_Ffs_Tree.type = FFS_FREE_SPACE
            New_Ffs_Tree.Data = New_Free_Space_Info
            Delete_Fv.insertChild(New_Ffs_Tree)
            Delete_Fv.Data.Free_Space = Add_Free_Space
        Delete_Fv.Child.remove(Delete_Ffs)
        Delete_Fv.Data.Header.FvLength = Used_Size + New_Free_Space
        Delete_Fv.Data.ModFvExt()
        Delete_Fv.Data.ModFvSize()
        Delete_Fv.Data.ModExtHeaderData()
        ModifyFvExtData(Delete_Fv)
        Delete_Fv.Data.ModCheckSum()
        # Recompress from the Fv node to update all the related node data.
        self.CompressData(Delete_Fv)
        self.Status = True
        logger.debug('Done!')
        return self.Status

    def ShrinkFv(self) -> bool:
        TargetFv = self.NewFfs
        TargetFv.Data.Data = b''
        if not TargetFv.Data.Free_Space:
            self.Status = True
        else:
            BlockSize = TargetFv.Data.Header.BlockMap[0].Length
            New_Free_Space = TargetFv.Data.Free_Space%BlockSize
            Removed_Space = TargetFv.Data.Free_Space - New_Free_Space
            TargetFv.Child[-1].Data.Data = b'\xff' * New_Free_Space
            TargetFv.Data.Size -= Removed_Space
            TargetFv.Data.Header.Fvlength = TargetFv.Data.Size
            ModifyFvSystemGuid(TargetFv)
            for item in TargetFv.Child:
                if item.type == FFS_FREE_SPACE:
                    TargetFv.Data.Data += item.Data.Data + item.Data.PadData
                else:
                    TargetFv.Data.Data += struct2stream(item.Data.Header)+ item.Data.Data + item.Data.PadData
            TargetFv.Data.ModFvExt()
            TargetFv.Data.ModFvSize()
            TargetFv.Data.ModExtHeaderData()
            ModifyFvExtData(TargetFv)
            TargetFv.Data.ModCheckSum()
            self.Status = True
        return self.Status
