pioneers/src/Importer/IQM/Parser.hs

180 lines
6.4 KiB
Haskell
Raw Normal View History

{-# LANGUAGE RankNTypes #-}
2014-04-16 13:45:14 +02:00
-- | Parser for IQM-Files
--
-- Assumes that the file is stored with 32-Bit-BigEndian-Ints
module Importer.IQM.Parser (parseIQM) where
import Importer.IQM.Types
import Data.Attoparsec.ByteString.Char8
import Data.Attoparsec.ByteString
import Data.ByteString.Char8 (pack)
import Data.ByteString (split, null)
import Data.Word
import Data.Int
import Unsafe.Coerce
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
import Control.Monad
import Prelude as P hiding (take, null)
2014-04-16 13:45:14 +02:00
-- | helper-function for creating an integral out of [8-Bit Ints]
w8ToInt :: Integral a => a -> a -> a
w8ToInt i add = 256*i + add
2014-04-16 13:45:14 +02:00
-- | shorthand-function for parsing Word8 into Integrals
parseNum :: (Integral a, Integral b) => [a] -> b
parseNum = (foldl1 w8ToInt) . map fromIntegral
2014-04-16 13:45:14 +02:00
-- | read a 16-Bit Int from Parsing-Input and log 2 bytes in our Parsing-Monad
--
-- begins with _ to defeat ghc-warnings. Rename if used!
_int16 :: CParser Int16
_int16 = do
ret <- lift $ do
a <- anyWord8 :: Parser Word8
b <- anyWord8 :: Parser Word8
return $ parseNum [b,a]
modify (+2)
return ret
2014-04-16 13:45:14 +02:00
-- | read a 32-Bit Int from Parsing-Input and log 4 bytes in our Parsing-Monad
int32 :: CParser Int32
int32 = do
ret <- lift $ do
a <- anyWord8 :: Parser Word8
b <- anyWord8 :: Parser Word8
c <- anyWord8 :: Parser Word8
d <- anyWord8 :: Parser Word8
return $ parseNum [d,c,b,a]
modify (+4)
return $ ret
2014-04-16 13:45:14 +02:00
-- | Parser for the header
readHeader :: CParser IQMHeader
readHeader = do
_ <- lift $ string (pack "INTERQUAKEMODEL\0")
v <- int32
2014-04-16 13:45:14 +02:00
-- when v /= 2 then --TODO: error something
size' <- int32
flags' <- int32
num_text' <- int32
2014-04-16 13:45:14 +02:00
ofs_text' <- int32
num_meshes' <- int32
2014-04-16 13:45:14 +02:00
ofs_meshes' <- int32
num_vertexarrays' <- int32
2014-04-16 13:45:14 +02:00
num_vertexes' <- int32
ofs_vertexarrays' <- int32
2014-04-16 13:45:14 +02:00
num_triangles' <- int32
ofs_triangles' <- int32
2014-04-16 13:45:14 +02:00
ofs_adjacency' <- int32
num_joints' <- int32
2014-04-16 13:45:14 +02:00
ofs_joints' <- int32
num_poses' <- int32
2014-04-16 13:45:14 +02:00
ofs_poses' <- int32
num_anims' <- int32
2014-04-16 13:45:14 +02:00
ofs_anims' <- int32
num_frames' <- int32
2014-04-16 13:45:14 +02:00
num_framechannels' <- int32
ofs_frames' <- int32
2014-04-16 13:45:14 +02:00
ofs_bounds' <- int32
num_comment' <- int32
2014-04-16 13:45:14 +02:00
ofs_comment' <- int32
num_extensions' <- int32
ofs_extensions' <- int32
return IQMHeader { version = v
, filesize = size'
, flags = flags'
, num_text = num_text'
, ofs_text = ofs_text'
, num_meshes = num_meshes'
, ofs_meshes = ofs_meshes'
, num_vertexarrays = num_vertexarrays'
, num_vertexes = num_vertexes'
, ofs_vertexarrays = ofs_vertexarrays'
, num_triangles = num_triangles'
, ofs_triangles = ofs_triangles'
, ofs_adjacency = ofs_adjacency'
, num_joints = num_joints'
, ofs_joints = ofs_joints'
, num_poses = num_poses'
, ofs_poses = ofs_poses'
, num_anims = num_anims'
, ofs_anims = ofs_anims'
, num_frames = num_frames'
, num_framechannels = num_framechannels'
, ofs_frames = ofs_frames'
, ofs_bounds = ofs_bounds'
, num_comment = num_comment'
, ofs_comment = ofs_comment'
, num_extensions = num_extensions'
, ofs_extensions = ofs_extensions'
}
2014-04-16 13:45:14 +02:00
-- | Parser for Mesh-Structure
readMesh :: CParser IQMMesh
readMesh = do
name <- int32
mat <- int32
fv <- int32
nv <- int32
ft <- int32
nt <- int32
return IQMMesh
{ meshName = if name == 0 then Nothing else Just (Mesh name)
, meshMaterial = mat
, meshFirstVertex = fv
, meshNumVertexes = nv
, meshFirstTriangle = ft
, meshNumTriangles = nt
}
2014-04-16 13:45:14 +02:00
-- | helper to read n consecutive Meshes tail-recursive
readMeshes :: Int -> CParser [IQMMesh]
readMeshes 1 = do
m <- readMesh
return [m]
readMeshes n = do
m <- readMesh
ms <- readMeshes (n-1)
return $ m:ms
2014-04-16 13:45:14 +02:00
-- | helper-Notation for subtracting 2 integral values of different kind in the precision
-- of the target-kind
(.-) :: forall a a1 a2.
(Num a, Integral a2, Integral a1) =>
a1 -> a2 -> a
(.-) a b = (fromIntegral a) - (fromIntegral b)
infix 5 .-
2014-04-16 13:45:14 +02:00
-- | skips (=drops) all input until the internal counter is at a given bytecount
--
-- Fails the parser if given bytecount is lower than the internal counter as we
-- read sequentially and do not do backtracking
skipToCounter :: Integral a => a -> CParser ()
skipToCounter a = do
let d = fromIntegral a
2014-04-16 13:45:14 +02:00
c <- get
when (d < c) $ fail "wanting to skip to counter already passed"
2014-04-16 13:45:14 +02:00
_ <- lift $ take $ d .- c
put d
2014-04-16 13:45:14 +02:00
-- | Parses an IQM-File and handles back the Haskell-Structure
parseIQM :: CParser IQM
parseIQM = do
2014-04-16 13:45:14 +02:00
put 0 --start at offset 0
h <- readHeader --read header
skipToCounter $ ofs_text h --skip 0-n bytes to get to text
text <- lift . take . fromIntegral $ num_text h --read texts
2014-04-16 13:45:14 +02:00
modify . (+) . fromIntegral $ num_text h --put offset forward
skipToCounter $ ofs_meshes h --skip 0-n bytes to get to meshes
meshes' <- readMeshes (fromIntegral (num_meshes h)) --read meshes
return IQM
{ header = h
, texts = filter (not.null) (split (unsafeCoerce '\0') text)
, meshes = meshes'
}
2014-04-16 13:45:14 +02:00