MSIX.MFR.ps1

# =============================================================================
# Modern File Redirection (MFR) Fixup
# -----------------------------------------------------------------------------
# MFRFixup is the next-generation file redirection DLL shipped in Tim Mangan's
# PSF fork. It is broadly compatible with FileRedirectionFixup but supports
# four different override modes per redirected path, plus an ILV-aware mode
# that respects the package's installedLocationVirtualization extension.
#
# Override modes (mirroring TMEditX):
# - Traditional classic VFS folder name (Local AppData, AppData,
# ProgramFilesX64, Windows, Fonts, …) — default for VFS paths
# - Local user-shell folder (ThisPCDesktopFolder, Personal,
# Common Desktop, Common Documents)
# - COW copy-on-write behaviour for PE files: default | enablePe |
# disableAll
# - IlvAware true|false — defer to package's
# uap10:installedLocationVirtualization extension
# =============================================================================

# Documented values that mirror the TMEditX UI
$script:MfrTraditionalKnownFolders = @(
    'Local AppData','AppData','LocalAppDataLow','Common AppData',
    'FOLDERID_System\Catroot2','FOLDERID_System\Catroot',
    'FOLDERID_System\drivers\etc','FOLDERID_System\driverstore',
    'FOLDERID_System\logfiles','FOLDERID_System\spool',
    'SystemX86','UserProgramFiles','ProgramFilesCommonX86','ProgramFilesX86',
    'SystemX64','ProgramFilesCommonX64','ProgramFilesX64',
    'System','Fonts','Windows\Microsoft.NET','Windows',
    'Common AppData\Microsoft\Windows\AppRepository',
    'Local AppData\Microsoft\Windows','Common Programs','Profile',
    'AppVPackageDrive'
)
$script:MfrLocalKnownFolders = @(
    'ThisPCDesktopFolder','Personal','Common Desktop','Common Documents'
)
$script:MfrCowOptions  = @('default','enablePe','disableAll')

function Get-MsixMfrKnownFolder {
    <#
    .SYNOPSIS
        Returns the documented MFR override folders by mode.
 
    .DESCRIPTION
        MFRFixup classifies redirected paths into two folder families:
        'Traditional' VFS-style folders (Local AppData, ProgramFilesX64, …)
        and 'Local' user-shell folders (Personal, Common Desktop, …). This
        function returns those documented values so callers can validate
        their MFR rules against the actual TMEditX-equivalent list rather
        than passing free-form strings.
 
        With -Mode Both (the default), an object with all three sets is
        returned (Traditional, Local, COW options).
 
    .PARAMETER Mode
        Which folder family to list. One of 'Traditional', 'Local', 'Both'
        (default).
 
    .OUTPUTS
        [string[]] when -Mode is Traditional or Local.
        [pscustomobject] with Traditional/Local/COW properties when -Mode
        is Both.
 
    .EXAMPLE
        Get-MsixMfrKnownFolder -Mode Traditional
 
    .EXAMPLE
        # Inspect all three sets in one go
        (Get-MsixMfrKnownFolder).COW
    #>

    [CmdletBinding()]
    [OutputType([object[]])]
    param(
        [ValidateSet('Traditional','Local','Both')]
        [string]$Mode = 'Both'
    )
    switch ($Mode) {
        'Traditional' { $script:MfrTraditionalKnownFolders }
        'Local'       { $script:MfrLocalKnownFolders }
        default       {
            [pscustomobject]@{
                Traditional = $script:MfrTraditionalKnownFolders
                Local       = $script:MfrLocalKnownFolders
                COW         = $script:MfrCowOptions
            }
        }
    }
}

function New-MsixMfrTraditionalRule {
    <#
    .SYNOPSIS
        Build a single Traditional-mode MFR redirection rule.
 
    .DESCRIPTION
        Produces a hashtable in the shape MFRFixup expects under
        config.redirectedPaths.traditionalRedirectedPaths[]. The known folder
        is validated against the documented Traditional set. The result is
        intended to be fed to New-MsixPsfMfrConfig.
 
    .PARAMETER KnownFolder
        One of the values from Get-MsixMfrKnownFolder -Mode Traditional.
 
    .PARAMETER RelativePath
        Path relative to that known folder (forward slashes, no leading slash).
 
    .PARAMETER Patterns
        Filename regex patterns to match (e.g. '.*\.log').
 
    .PARAMETER Cow
        Copy-on-write behaviour for PE files: default | enablePe | disableAll.
 
    .PARAMETER IlvAware
        If true, this rule respects uap10:installedLocationVirtualization.
 
    .OUTPUTS
        [hashtable] (ordered) suitable for New-MsixPsfMfrConfig
        -TraditionalRules.
 
    .EXAMPLE
        New-MsixMfrTraditionalRule -KnownFolder 'ProgramFilesX64' `
            -RelativePath 'Contoso/logs' -Patterns '.*\.log' -Cow enablePe
    #>

    [OutputType([hashtable])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param(
        [Parameter(Mandatory)]
        [ValidateScript({
            if ($_ -in $script:MfrTraditionalKnownFolders) { $true }
            else { throw "Unknown traditional folder '$_'. Use Get-MsixMfrKnownFolder -Mode Traditional." }
        })]
        [string]$KnownFolder,
        [Parameter(Mandatory)]
        [string]$RelativePath,
        [Parameter(Mandatory)]
        [string[]]$Patterns,
        [ValidateSet('default','enablePe','disableAll')]
        [string]$Cow,
        [bool]$IlvAware
    )
    $r = [ordered]@{
        knownFolder    = $KnownFolder
        relativePath   = $RelativePath
        patterns       = [array]$Patterns
    }
    if ($Cow)               { $r['copyOnWrite']  = $Cow }
    if ($PSBoundParameters.ContainsKey('IlvAware')) { $r['ilvAware'] = $IlvAware }
    return $r
}


function New-MsixMfrLocalRule {
    <#
    .SYNOPSIS
        Build a single Local-mode MFR redirection rule (user-shell folders).
 
    .DESCRIPTION
        Produces a hashtable in the shape MFRFixup expects under
        config.redirectedPaths.localRedirectedPaths[]. Use this for redirected
        paths that live under user-shell folders (Desktop, Documents, …)
        instead of the classic VFS roots covered by
        New-MsixMfrTraditionalRule.
 
    .PARAMETER KnownFolder
        One of: ThisPCDesktopFolder, Personal, Common Desktop, Common Documents.
 
    .PARAMETER RelativePath
        Path relative to that known folder.
 
    .PARAMETER Patterns
        Filename regex patterns to match.
 
    .PARAMETER Cow
        Copy-on-write behaviour for PE files: default | enablePe | disableAll.
 
    .PARAMETER IlvAware
        If true, this rule respects uap10:installedLocationVirtualization.
 
    .OUTPUTS
        [hashtable] (ordered) suitable for New-MsixPsfMfrConfig -LocalRules.
 
    .EXAMPLE
        New-MsixMfrLocalRule -KnownFolder 'Personal' `
            -RelativePath 'Contoso' -Patterns '.*\.cfg'
    #>

    [OutputType([hashtable])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param(
        [Parameter(Mandatory)]
        [ValidateScript({
            if ($_ -in $script:MfrLocalKnownFolders) { $true }
            else { throw "Unknown local folder '$_'. Use Get-MsixMfrKnownFolder -Mode Local." }
        })]
        [string]$KnownFolder,
        [Parameter(Mandatory)]
        [string]$RelativePath,
        [Parameter(Mandatory)]
        [string[]]$Patterns,
        [ValidateSet('default','enablePe','disableAll')]
        [string]$Cow,
        [bool]$IlvAware
    )
    $r = [ordered]@{
        knownFolder  = $KnownFolder
        relativePath = $RelativePath
        patterns     = [array]$Patterns
    }
    if ($Cow)               { $r['copyOnWrite'] = $Cow }
    if ($PSBoundParameters.ContainsKey('IlvAware')) { $r['ilvAware'] = $IlvAware }
    return $r
}


function New-MsixPsfMfrConfig {
    <#
    .SYNOPSIS
        Builds an MFRFixup config hashtable for use with Add-MsixPsfV2.
 
    .DESCRIPTION
        MFRFixup ships in Tim Mangan's PSF fork (`MFRFixup64.dll` /
        `MFRFixup32.dll`) and is a drop-in replacement for FileRedirectionFixup
        with finer-grained controls. This helper produces the standard config
        block expected at config.json -> processes[].fixups[].
 
    .PARAMETER TraditionalRules
        Hashtables produced by New-MsixMfrTraditionalRule.
 
    .PARAMETER LocalRules
        Hashtables produced by New-MsixMfrLocalRule.
 
    .PARAMETER GlobalIlvAware
        Default value for the ilvAware flag at the top level (overridable per rule).
 
    .PARAMETER GlobalCow
        Default copyOnWrite mode at the top level.
 
    .OUTPUTS
        [hashtable] with keys 'dll' (always 'MFRFixup.dll') and 'config'
        (the ordered hashtable assembled from the supplied rules). Consumed
        by Add-MsixPsfV2 -Fixups.
 
    .EXAMPLE
        # Builder -> Add-MsixPsfV2 chain
        $rule = New-MsixMfrTraditionalRule -KnownFolder 'ProgramFilesX64' `
            -RelativePath 'Contoso/logs' -Patterns '.*\.log' -Cow enablePe
 
        $mfr = New-MsixPsfMfrConfig -TraditionalRules @($rule) -GlobalIlvAware $true
 
        Add-MsixPsfV2 -PackagePath .\app.msix -Fixups @($mfr) `
            -Pfx .\cert.pfx -PfxPassword (Read-Host -AsSecureString)
    #>

    [OutputType([hashtable])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param(
        [hashtable[]]$TraditionalRules,
        [hashtable[]]$LocalRules,
        [bool]$GlobalIlvAware,
        [ValidateSet('default','enablePe','disableAll')]
        [string]$GlobalCow
    )

    $config = [ordered]@{}
    if ($PSBoundParameters.ContainsKey('GlobalIlvAware')) { $config['ilvAware']    = $GlobalIlvAware }
    if ($GlobalCow)                                       { $config['copyOnWrite'] = $GlobalCow }

    $rules = @{}
    if ($TraditionalRules) { $rules['traditionalRedirectedPaths'] = @($TraditionalRules) }
    if ($LocalRules)       { $rules['localRedirectedPaths']       = @($LocalRules) }

    if ($rules.Count -gt 0) {
        $config['redirectedPaths'] = $rules
    }

    return @{
        dll    = 'MFRFixup.dll'
        config = $config
    }
}


# Backward-compatible plural aliases
Set-Alias Get-MsixMfrKnownFolders Get-MsixMfrKnownFolder