hascar-0.1.1.0: src/Codec/Archive/SAPCAR/FlatedFile.hs
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module: FlatedFile
-- Copyright: (C) 2015-2016, Virtual Forge GmbH
-- License: GPL2
-- Maintainer: Hans-Christian Esperer <hans-christian.esperer@virtualforge.com>
-- Stability: experimental
-- Portability: portable
-- |
-- (De-)compress SAPCAR files
--
-- Copyright (C) 2016, Virtual Forge GmbH
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or (at
-- your option) any later version.
--
-- This program is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-- USA
module Codec.Archive.SAPCAR.FlatedFile
( decompressBlock
) where
import Control.Applicative
import Control.Monad
import Control.Monad.State.Strict
import Data.Char
import Data.Foldable (toList)
import Data.Functor.Identity
import Data.Sequence ((><), (|>))
import Data.Word
import System.IO
import qualified Control.Exception as CE
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Sequence as DS
import Codec.Archive.SAPCAR.BitStream
import Codec.Archive.SAPCAR.CanonicalHuffmanTree
import Codec.Archive.SAPCAR.FlexibleUtils
-- Copied from vpa108csulzh.cpp under GPL by SAP AG
border :: [Int]
border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]
cplens :: [Int]
cplens = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43,
51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0]
cpdist :: [Int]
cpdist = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257,
385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289,
16385, 24577]
csExtraDistBits :: [Int]
csExtraDistBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]
csExtraLenBits :: [Int]
csExtraLenBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3,
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99]
-- End copied from vpa108csulzh.cpp under GPL by SAP AG
readInt32Big :: Handle -> IO Int
readInt32Big h = do
[b1, b2, b3, b4] <- replicateM 4 $ ord <$> hGetChar h
return $ b1 * 16777216 + b2 * 65536 + b3 * 256 + b4
entryReader :: [[Int]]
-> CanonicalHuffmanTree
-> Int
-> Int
-> StateT
BitStream Data.Functor.Identity.Identity [Int]
entryReader entries huft entriesToRead lastEntry
| (length . concat $ entries) >= entriesToRead = return . concat . reverse $ entries
| otherwise = do
entry <- readEntry huft
newEntries <- handleEntry entry
entryReader (newEntries:entries) huft entriesToRead $ last newEntries
where
handleEntry :: Int -> State BitStream [Int]
handleEntry code
| code < 16 = return [code]
| code == 16 = do
numRepetitions <- (3 +) <$> getAndConsume 2
return $ replicate numRepetitions lastEntry
| code == 17 = do
numZeroes <- (3 +) <$> getAndConsume 3
return $ replicate numZeroes 0
| code == 18 = do
numZeroes <- (11 +) <$> getAndConsume 7
return $ replicate numZeroes 0
| otherwise = error "Corrupted file"
decodeIt
:: CanonicalHuffmanTree
-> CanonicalHuffmanTree
-> DS.Seq Word8
-> State BitStream (DS.Seq Word8)
-- decodeIt lt dt = BS.pack . toList <$> decodeIt' empty
decodeIt lt dt = decodeIt'
where
decodeIt' acc = do
entry <- readEntryRaw lt
case numExtraBits entry of
n | n == eobcode -> return acc
n | n == litcode -> decodeIt' $ acc |> (fromIntegral $ value entry)
n | n > litcode -> error "Sonderfall not handled"
_ -> do
-- n <- (+ value entry) <$> getAndConsume (numExtraBits entry - 16)
n <- (+ value entry) <$> getAndConsume (numExtraBits entry)
distEntry <- readEntryRaw dt
dist <- (+ value distEntry) <$> getAndConsume (numExtraBits distEntry)
let new = DS.drop (length acc - dist) acc
new' = foldl (\a _ -> a >< new) empty [0..m]
m = n `div` dist
l = (acc >< (DS.take n new'))
decodeIt' l
-- |Decompress one or more lzh compressed blocks
decompressBlock :: BS.ByteString -> BS.ByteString
decompressBlock inp = blocks
where
blocks = evalState decompressor . makeStream $ inp
skipNonsenseBits :: State BitStream ()
skipNonsenseBits = do
numNonsenseBits <- getAndConsume 2
when (numNonsenseBits > 0) $
void $ getAndConsume numNonsenseBits
decompressor :: State BitStream BS.ByteString
decompressor = skipNonsenseBits >> (BS.pack . toList <$> decompressor' empty)
decompressor' :: DS.Seq Word8 -> State BitStream (DS.Seq Word8)
decompressor' acc = do
lastBlock <- getAndConsume 1
blockType <- getAndConsume 2
res <- case blockType of
1 -> decompressStaticBlock acc
2 -> decompressDynamicBlock acc
_ -> error $ "Block type " ++ show blockType ++ " not supported!"
case lastBlock of
1 -> return res
0 -> decompressor' res
decompressDynamicBlock :: DS.Seq Word8 -> State BitStream (DS.Seq Word8)
decompressDynamicBlock acc = do
numLiterals <- (+ 257) <$> getAndConsume 5
numDistanceCodes <- (+ 1) <$> getAndConsume 5
numBitLengths <- (+ 4) <$> getAndConsume 4
let bitLengthPositions = Prelude.take numBitLengths border
bitLengths' <- mapM (\blp -> (,) blp <$> getAndConsume 3) bitLengthPositions
let bitLengths = makeFlexList (0, 18) 0 bitLengths'
huft = makeHuffmanTree bitLengths 19 [] []
entriesToRead = numLiterals + numDistanceCodes
ll <- entryReader [] huft entriesToRead (-1)
let lengthCodes = take numLiterals ll
distCodes = take numDistanceCodes $ drop numLiterals ll
lengthTree = makeHuffmanTree lengthCodes 257 cplens csExtraLenBits
distTree = makeHuffmanTree distCodes 0 cpdist csExtraDistBits
decodeIt lengthTree distTree acc
decompressStaticBlock :: DS.Seq Word8 -> State BitStream (DS.Seq Word8)
decompressStaticBlock acc = do
-- Length and dist codes copied from vpa108csulzh.cpp under GPL by SAP AG
let lengthCodes = replicate 144 8 ++ replicate 112 9 ++ replicate 24 7 ++ replicate 8 8
distCodes = replicate 30 5
-- End length and dist codes copied from vpa108csulzh.cpp under GPL by SAP AG
let lengthTree = makeHuffmanTree lengthCodes 257 cplens csExtraLenBits
distTree = makeHuffmanTree distCodes 0 cpdist csExtraDistBits
decodeIt lengthTree distTree acc