hascar-0.1.0.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.Attoparsec.ByteString.Lazy as DABL
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