Param(
        [Parameter(Mandatory=$true)][String[]]$FTLFileName,
        [Parameter(Mandatory=$true)][String[]]$OutputBaseFileName,
        [Switch]$GenerateParts,
        [Switch]$PrintDebugInformation
)

$FTLHeader = @()
$FTLSegments = @()
$FTLJUMPSegments = @()
$FTLDATASegments = @()
$FTLCODESegments = @()

$FTLHeader += "FTLFileName`tMagic`tChecksum`tUnknown1`tc_6`tc_7`ti_8`tc_0a`tc_0b`tc_0c`tc_0d`tDate1`tDate2`tDate1Text`tDate2Text`tSegmentCount`tComputedHeaderChecksum"
$FTLSegments += "FTLFileName`tSegmentIndex`tType`Id`tOffset`tSize"
$FTLJUMPSegments += "FTLFileName`tAboveA5Size`tBelowA5Size`tJumpTableSize`tJumpTableOffset`tStackSize`tCODESize`tDATASegmentSize`tUnknown2`tUnknown3`tUnknown4`tJUMPChecksum`tCODEChecksum`tDATAChecksum`tComputedJUMPChecksum"
$FTLDATASegments += "FTLFileName`ta`tDataCompressedSize`tRelocationDataSize`tDATAChecksum`tComputedDATAChecksum"
$FTLCODESegments += "FTLFileName`tCodeWord1`tCodeWord2`tIterationCount`tCODEChecksum`tComputedCODEChecksum"

function GetNibble {
        if ($Global:NibbleInByte) {
                $ExtractedNibble = ($FTLFileBytes[$Global:NibbleOffset] -band 15)
                $Global:NibbleOffset++
                $Global:NibbleInByte = 0
        } else {
                $ExtractedNibble = ($FTLFileBytes[$Global:NibbleOffset] -shr 4)
                $Global:NibbleInByte = 1
        }
        # Write-Host "Nibble at $($Global:NibbleOffset): $ExtractedNibble"
        return $ExtractedNibble
}

$Global:NibbleInByte = 0
$Global:NibbleOffset = -1

$FTLFileBytes = Get-Content -Encoding Byte -Path $FTLFileName

# Header
$Magic = $($FTLFileBytes[0] * 256 + $FTLFileBytes[1])
$Checksum = $($FTLFileBytes[2] * 256 + $FTLFileBytes[3])
$Unknown1 = $($FTLFileBytes[4] * 256 + $FTLFileBytes[5])
$c_6 = $FTLFileBytes[6]
$c_7 = $FTLFileBytes[7]
$i_8 = $($FTLFileBytes[8] * 256 + $FTLFileBytes[9])
$c_0a = $FTLFileBytes[10]
$c_0b = $FTLFileBytes[11]
$c_0c = $FTLFileBytes[12]
$c_0d = $FTLFileBytes[13]
$Date1 = $($FTLFileBytes[14] * 256 + $FTLFileBytes[15])
$Date1Text = Get-Date -Day (($Date1 -band 0x001F) + 1) -Month ((($Date1 -band 0x01E0) -shr 5) + 1) -Year ((($Date1 -band 0xFE00) -shr 9) + 1980) -Format yyyy-MM-dd
$Date2 = $($FTLFileBytes[16] * 256 + $FTLFileBytes[17])
$Date2Text = Get-Date -Day (($Date2 -band 0x001F) + 1) -Month ((($Date2 -band 0x01E0) -shr 5) + 1) -Year ((($Date2 -band 0xFE00) -shr 9) + 1980) -Format yyyy-MM-dd
$SegmentCount = $($FTLFileBytes[18] * 256 + $FTLFileBytes[19])

$ComputedHeaderChecksum = 0
for ($i = 4; $i -lt 20; $i++) {
        $ComputedHeaderChecksum += $FTLFileBytes[$i] * $i
        $ComputedHeaderChecksum %= 65535
}
for ($i = 0; $i -lt $SegmentCount; $i++) {
        for ($j = 0; $j -lt 12; $j++) {
                $ComputedHeaderChecksum += $FTLFileBytes[20 + 12 * $i + $j] * ($j + 12 * $i + 1)
                $ComputedHeaderChecksum %= 65535
        }
}
$FTLHeader += "$FTLFileName`t0x$("{0:X4}" -f $Magic)`t0x$("{0:X4}" -f $Checksum)`t0x$("{0:X4}" -f $Unknown1)`t0x$("{0:X2}" -f $c_6)`t0x$("{0:X2}" -f $c_7)`t0x$("{0:X4}" -f $i_8)`t0x$("{0:X2}" -f $c_0a)`t0x$("{0:X2}" -f $c_0b)`t0x$("{0:X2}" -f $c_0c)`t0x$("{0:X2}" -f $c_0d)`t0x$("{0:X4}" -f $Date1)`t0x$("{0:X4}" -f $Date2)`t$Date1Text`t$Date2Text`t$SegmentCount`t0x$("{0:X4}" -f $ComputedHeaderChecksum)"

for($SegmentIndex = 0; $SegmentIndex -lt $SegmentCount; $SegmentIndex++) {
        $Type = $($FTLFileBytes[12 * $SegmentIndex + 20] * 256 + $FTLFileBytes[12 * $SegmentIndex + 21])
        $Id = $($FTLFileBytes[12 * $SegmentIndex + 22] * 256 + $FTLFileBytes[12 * $SegmentIndex + 23])
        $Offset = $($FTLFileBytes[12 * $SegmentIndex + 24] * 256 + $FTLFileBytes[12 * $SegmentIndex + 25]) * 65536 + $($FTLFileBytes[12 * $SegmentIndex + 26] * 256 + $FTLFileBytes[12 * $SegmentIndex + 27])
        $Size = $($FTLFileBytes[12 * $SegmentIndex + 28] * 256 + $FTLFileBytes[12 * $SegmentIndex + 29]) * 65536 + $($FTLFileBytes[12 * $SegmentIndex + 30] * 256 + $FTLFileBytes[12 * $SegmentIndex + 31])
        
        $FTLSegments += "$FTLFileName`t$SegmentIndex`t0x$("{0:X4}" -f $Type)`t0x$("{0:X4}" -f $Id)`t0x$("{0:X8}" -f $Offset)`t0x$("{0:X8}" -f $Size)"
        
        if ($type -eq 0x10) {
                $AboveA5Size = ($($FTLFileBytes[$Offset + 0] * 256 + $FTLFileBytes[$Offset + 1])) * 65536 + $($FTLFileBytes[$Offset + 2] * 256 + $FTLFileBytes[$Offset + 3])
                $BelowA5Size = ($($FTLFileBytes[$Offset + 4] * 256 + $FTLFileBytes[$Offset + 5])) * 65536 + $($FTLFileBytes[$Offset + 6] * 256 + $FTLFileBytes[$Offset + 7])
                $JumpTableSize = ($($FTLFileBytes[$Offset + 8] * 256 + $FTLFileBytes[$Offset + 9])) * 65536 + $($FTLFileBytes[$Offset + 10] * 256 + $FTLFileBytes[$Offset + 11])
                $JumpTableOffset = ($($FTLFileBytes[$Offset + 12] * 256 + $FTLFileBytes[$Offset + 13])) * 65536 + $($FTLFileBytes[$Offset + 14] * 256 + $FTLFileBytes[$Offset + 15])
                $StackSize = ($($FTLFileBytes[$Offset + 16] * 256 + $FTLFileBytes[$Offset + 17])) * 65536 + $($FTLFileBytes[$Offset + 18] * 256 + $FTLFileBytes[$Offset + 19])
                $CODESize = ($($FTLFileBytes[$Offset + 20] * 256 + $FTLFileBytes[$Offset + 21])) * 65536 + $($FTLFileBytes[$Offset + 22] * 256 + $FTLFileBytes[$Offset + 23])
                $DATASegmentSize = ($($FTLFileBytes[$Offset + 24] * 256 + $FTLFileBytes[$Offset + 25])) * 65536 + $($FTLFileBytes[$Offset + 26] * 256 + $FTLFileBytes[$Offset + 27])
                $Unknown2 = $($FTLFileBytes[$Offset + 28] * 256 + $FTLFileBytes[$Offset + 29])
                $CODECompressed = $($FTLFileBytes[$Offset + 30] * 256 + $FTLFileBytes[$Offset + 31])
                $Unknown4 = $($FTLFileBytes[$Offset + 32] * 256 + $FTLFileBytes[$Offset + 33])
                $JUMPChecksum = $($FTLFileBytes[$Offset + 34] * 256 + $FTLFileBytes[$Offset + 35])
                $CODEChecksum = $($FTLFileBytes[$Offset + 36] * 256 + $FTLFileBytes[$Offset + 37])
                $DATAChecksum = $($FTLFileBytes[$Offset + 38] * 256 + $FTLFileBytes[$Offset + 39])

                $ComputedJUMPChecksum = 0
                for ($i = 0; $i -lt $Size; $i += 2) {
                        $ComputedJUMPChecksum += $FTLFileBytes[$Offset + $i] * 256 + $FTLFileBytes[$Offset + $i + 1]
                        $ComputedJUMPChecksum %= 65536
                }
                $ComputedJUMPChecksum += 65536 - $JUMPChecksum
                $ComputedJUMPChecksum %= 65536
                
                $AboveA5 = New-Object Byte[] $AboveA5Size
                $ByteIndex = 0
                for ($i = 0; $i -lt $JumpTableOffset; $i++) { # 32 bytes of 'Application Parameters' preceding the jumptable
                        $AboveA5[$ByteIndex++] = 0xA5     # Arbitrary value to fill space
                }
                $JumpEntryCount = $JumpTableSize / 8
                for ($i = 0; $i -lt $JumpEntryCount; $i++) {
                        $AboveA5[$ByteIndex++] = 0
                        $AboveA5[$ByteIndex++] = 0
                        $AboveA5[$ByteIndex++] = 0x4E
                        $AboveA5[$ByteIndex++] = 0xF9
                        $AboveA5[$ByteIndex++] = $FTLFileBytes[$Offset + 40 + 4 * $i]
                        $AboveA5[$ByteIndex++] = $FTLFileBytes[$Offset + 40 + 4 * $i + 1]
                        $AboveA5[$ByteIndex++] = $FTLFileBytes[$Offset + 40 + 4 * $i + 2]
                        $AboveA5[$ByteIndex++] = $FTLFileBytes[$Offset + 40 + 4 * $i + 3]
                }

                $FTLJUMPSegments += "$FTLFileName`t0x$("{0:X8}" -f $AboveA5Size)`t0x$("{0:X8}" -f $BelowA5Size)`t0x$("{0:X8}" -f $JumpTableSize)`t0x$("{0:X8}" -f $JumpTableOffset)`t0x$("{0:X8}" -f $StackSize)`t0x$("{0:X8}" -f $CODESize)`t0x$("{0:X8}" -f $DATASegmentSize)`t0x$("{0:X4}" -f $Unknown2)`t0x$("{0:X4}" -f $CODECompressed)`t0x$("{0:X4}" -f $Unknown4)`t0x$("{0:X4}" -f $JUMPChecksum)`t0x$("{0:X4}" -f $CODEChecksum)`t0x$("{0:X4}" -f $DATAChecksum)`t0x$("{0:X4}" -f $ComputedJUMPChecksum)"
        }
        if ($type -eq 0x11) {
                $a = $($FTLFileBytes[$Offset + 0] * 256 + $FTLFileBytes[$Offset + 1])
                $DataCompressedSize = ($($FTLFileBytes[$Offset + 2] * 256 + $FTLFileBytes[$Offset + 3])) * 65536 + $($FTLFileBytes[$Offset + 4] * 256 + $FTLFileBytes[$Offset + 5])
                $RelocationDataSize =  ($($FTLFileBytes[$Offset + 6] * 256 + $FTLFileBytes[$Offset + 7])) * 65536 + $($FTLFileBytes[$Offset + 8] * 256 + $FTLFileBytes[$Offset + 9])

                if ($GenerateParts) {
                        $CompressedDATA = New-Object Byte[] $DataCompressedSize
                        $ByteIndex = 0
                        for ($i = 10; $i -lt $DataCompressedSize + 10; $i++) {
                                $CompressedDATA[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + $i]
                        }
                        Set-Content -Encoding Byte -Value $CompressedDATA -Path "$($OutputBaseFileName)_Compressed_DATA"
                }
                
                $ComputedDATAChecksum = 0
                for ($i = 0; $i -lt $Size; $i++) {
                        $ComputedDATAChecksum += $FTLFileBytes[$Offset + $i]
                        $ComputedDATAChecksum %= 65536
                }

                $FTLDATASegments += "$FTLFileName`t0x$("{0:X4}" -f $a)`t0x$("{0:X8}" -f $DataCompressedSize)`t0x$("{0:X8}" -f $RelocationDataSize)`t0x$("{0:X4}" -f $DATAChecksum)`t0x$("{0:X4}" -f $ComputedDATAChecksum)"
                
                $UncompressedDATA = New-Object Byte[] $BelowA5Size
                $ByteIndex = 0
                $Consecutive0Count = 0
                $Repeat0 = -1
                for ($i = 10; $i -lt $DataCompressedSize + 10; $i++) {
                        if ($Consecutive0Count -eq 2) {
                                if ($Repeat0 -eq -1) {
                                        $Repeat0 = $FTLFileBytes[$Offset + $i]
                                } else {
                                        $Repeat0 = $Repeat0 * 256 + $FTLFileBytes[$Offset + $i]
                                        for ($j = 0; $j -lt $Repeat0; $j++) {
                                                $UncompressedDATA[$ByteIndex++] = [Byte]0
                                        }
                                        $Repeat0 = -1
                                        $Consecutive0Count = 0
                                }
                        } else {
                                $UncompressedDATA[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + $i]
                                if ($FTLFileBytes[$Offset + $i] -eq 0) {
                                        if (((($Offset + $i) -band 1) -eq 0) -or ($Consecutive0Count -ne 0)) {
                                                $Consecutive0Count++
                                        }
                                } else {
                                        $Consecutive0Count = 0
                                }
                        }
                }
                
                if ($RelocationDataSize) {
                        $UncompressedDREL = New-Object Byte[] $RelocationDataSize
                        $ByteIndex = 0
                        for ($i = 0; $i -lt $RelocationDataSize; $i++) {
                                $UncompressedDREL[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + 10 + $DataCompressedSize + $i]
                        }
                        Set-Content -Encoding Byte -Value $UncompressedDREL -Path "$($OutputBaseFileName)_DREL"
                }
        }
        if ($type -eq 0x12) {
                $CodeWord1 = $($FTLFileBytes[$Offset + 0] * 256 + $FTLFileBytes[$Offset + 1])
                $CodeWord2 = $($FTLFileBytes[$Offset + 2] * 256 + $FTLFileBytes[$Offset + 3])
                $CompressedCODEWordCount = ($FTLFileBytes[$Offset + 4] * 256 + $FTLFileBytes[$Offset + 5]) * 65536 + ($FTLFileBytes[$Offset + 6] * 256 + $FTLFileBytes[$Offset + 7])
                
                $Global:NibbleInByte = 0
                $Global:NibbleOffset = $Offset + 3848
                $UncompressedCODE = New-Object Byte[] $CODESize
                $ByteIndex = 0
                if ($CODECompressed) {
                        for ($i = 0; $i -lt $CompressedCODEWordCount; $i++) {
                                $nibble = $(GetNibble)
                                if ($nibble -eq 15) {
                                        $n1 = ($(GetNibble) * 16 + $(GetNibble))
                                        $n2 = ($(GetNibble) * 16 + $(GetNibble))
                                        $UncompressedCODE[$ByteIndex++] = [Byte]$n1
                                        $UncompressedCODE[$ByteIndex++] = [Byte]$n2
                # Write-Host $("{0:X4}" -f $($n1 * 256 + $n2))
                                } else {
                                        if ($nibble -ge 8) {
                                                $FrequentWordIndex = ($nibble * 16 + $(GetNibble)) * 16 + $(GetNibble)
                                                $FrequentWordIndex = $FrequentWordIndex - 1920
                                                $UncompressedCODE[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2)]
                                                $UncompressedCODE[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2) + 1]
                # Write-Host $("{0:X4}" -f $($FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2)] * 256 + $FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2) + 1]))
                                        } else {
                                                $FrequentWordIndex = $nibble * 16 + $(GetNibble)
                                                $UncompressedCODE[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2)]
                                                $UncompressedCODE[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2) + 1]
                # Write-Host $("{0:X4}" -f $($FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2)] * 256 + $FTLFileBytes[$Offset + 8 + ($FrequentWordIndex * 2) + 1]))
                                        }
                                }
                        }
                } else {
                        for ($i = 0; $i -lt $Size; $i++) {
                                $UncompressedCODE[$ByteIndex++] = [Byte]$FTLFileBytes[$Offset + $i]
                        }
                }
                
                $ComputedCODEChecksum = 0
                for ($i = 0; $i -lt $CODESize; $i++) {
                        $ComputedCODEChecksum += $UncompressedCODE[$i]
                        $ComputedCODEChecksum %= 65536
                }
                
                $FTLCODESegments += "$FTLFileName`t0x$("{0:X4}" -f $CodeWord1)`t0x$("{0:X4}" -f $CodeWord2)`t0x$("{0:X8}" -f $CompressedCODEWordCount)`t0x$("{0:X4}" -f $CODEChecksum)`t0x$("{0:X4}" -f $ComputedCODEChecksum)"
        }
}

Set-Content -Encoding Byte -Value $AboveA5 -Path "$($OutputBaseFileName)_JUMPTABLE"
Set-Content -Encoding Byte -Value $UncompressedDATA -Path "$($OutputBaseFileName)_DATA"
Set-Content -Encoding Byte -Value $UncompressedCODE -Path "$($OutputBaseFileName)_CODE"

if ($PrintDebugInformation) {
        "FTLHeader ----------------------------------------------"
        $FTLHeader
        "FTLSegments --------------------------------------------"
        $FTLSegments
        "FTLJUMPSegments ----------------------------------------"
        $FTLJUMPSegments
        "FTLDATASegments ----------------------------------------"
        $FTLDATASegments
        "FTLCODESegments ----------------------------------------"
        $FTLCODESegments
}
