packages feed

immutaball-core-0.1.0.4.1: Immutaball/Share/Level/Base.hs

{-# OPTIONS_GHC -fno-warn-tabs #-}  -- Support tab indentation better, for a better default of no warning if tabs are used: https://dmitryfrank.com/articles/indent_with_tabs_align_with_spaces .
-- Enable warnings:
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

-- Level/Base.hs.

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, NondecreasingIndentation #-}

module Immutaball.Share.Level.Base
	(
		-- * sol
		solPathMax,
		solMagicConstant,
		solVersionCurr,
		solVersion2024_04,
		pathFlagOriented,
		pathFlagParented,
		mtrlFlagAlphaTest,
		lumpFlagDetail,
		Mtrl(..), mtrlD, mtrlA, mtrlS, mtrlE, mtrlH, mtrlAngle, mtrlFl, mtrlF,
			mtrlAlphaFunc, mtrlAlphaRef,
		Vert(..), vertP,
		Edge(..), edgeVi, edgeVj,
		Side(..), sideN, sideD,
		Texc(..), texcU,
		Offs(..), offsTi, offsSi, offsVi,
		Geom(..), geomMi, geomOi, geomOj, geomOk,
		Lump(..), lumpFl, lumpV0, lumpVc, lumpE0, lumpEc, lumpG0, lumpGc,
			lumpS0, lumpSc,
		Node(..), nodeSi, nodeNi, nodeNj, nodeL0, nodeLc,
		Path(..), pathP, pathE, pathT, pathTm, pathPi, pathF, pathS, pathFl,
			pathP0, pathP1,
		Body(..), bodyP0, bodyP1, bodyNi, bodyL0, bodyLc, bodyG0, bodyGc,
		Item(..), itemP, itemT, itemN, itemP0, itemP1,
		Goal(..), goalP, goalR, goalP0, goalP1,
		Jump(..), jumpP, jumpQ, jumpR, jumpP0, jumpP1,
		Swch(..), swchP, swchR, swchPi, swchT, swchTm, swchF, swchI, swchP0, swchP1,
		Bill(..), billFl, billMi, billT, billD, billW, billH, billRx, billRy,
			billRz, billP, billP0, billP1,
		Ball(..), ballP, ballR,
		View(..), viewP, viewQ,
		Dict(..), dictAi, dictAj,
		Sol(..), solMagic, solVersion, solAc, solDc, solMc, solVc, solEc,
			solSc, solTc, solOc, solGc, solLc, solNc, solPc, solBc, solHc,
			solZc, solJc, solXc, solRc, solUc, solWc, solIc, solAv, solDv,
			solMv, solVv, solEv, solSv, solTv, solOv, solGv, solLv, solNv,
			solPv, solBv, solHv, solZv, solJv, solXv, solRv, solUv, solWv,
			solIv,
		LevelIB,
		emptySol,

		-- * Optional low-level storable provisions (we don't use these)
		peeki32Native,
		peeki32BE,
		peeki32LE,
		peekf32dLE,
		peekn,
		peeknWith,
		peekCString,
		pokei32Native,
		pokei32BE,
		pokei32LE,
		pokef32dLE,
		poken,
		pokenWith,
		pokeCString,
		asType,
		sizeOfMtrl,
		sizeOfMtrlMin,
		sizeOfMtrlMax,
		sizeOfExistingMtrl,
		sizeOfPath,
		sizeOfPathMin,
		sizeOfPathMax,
		sizeOfExistingPath,
		sizeOfEmptySol,
		sizeOfExistingSolMin,
		sizeOfExistingSolMax,
		sizeOfExistingSol,
		peekSol,
		peekSolLengths,
		pokeSol
	) where

import Prelude ()
import Immutaball.Prelude

import Control.Arrow
import Control.Monad
import Control.Monad.Fix
import Data.Bits
import Data.Coerce
--import Data.Function hiding (id, (.))
import Data.Int
import Data.Word
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable

import Control.Lens
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Data.Array
import Data.Array.IO
import Data.Array.IArray as IA
--import Data.Array.MArray as MA
--import Data.Array.Unboxed

import Immutaball.Share.Math
import Immutaball.Share.Utils

-- * sol

solPathMax :: Int
solPathMax = 64

solMagicConstant :: Int32
solMagicConstant = 0x4C4F53AF  -- LE encoding of AF 'S' 'O' 'L'.

solVersionCurr :: Int32
solVersionCurr = solVersion2024_04

solVersion2024_04 :: Int32
solVersion2024_04 = 9

pathFlagOriented :: Int32
pathFlagOriented = 1

pathFlagParented :: Int32
pathFlagParented = 2

mtrlFlagAlphaTest :: Int32
mtrlFlagAlphaTest = 1 `shiftL` 9

lumpFlagDetail :: Int32
lumpFlagDetail = 1

data Mtrl = Mtrl {
	-- | Diffuse color.
	_mtrlD :: Vec4 Double,
	-- | Ambient color.
	_mtrlA :: Vec4 Double,
	-- | Specular color.
	_mtrlS :: Vec4 Double,
	-- | Emmission color.
	_mtrlE :: Vec4 Double,
	-- | Specular exponent.
	_mtrlH :: Double,

	_mtrlAngle :: Double,

	-- | Material flags.
	_mtrlFl :: Int32,

	-- | Texture file name.
	_mtrlF :: String,

	-- | Comparison function.
	_mtrlAlphaFunc :: Int32,
	_mtrlAlphaRef :: Double
}
	deriving (Eq, Ord, Show)
makeLenses ''Mtrl

data Vert = Vert {
	-- | Vertex position.
	_vertP :: Vec3 Double
}
	deriving (Eq, Ord, Show)
makeLenses ''Vert

data Edge = Edge {
	_edgeVi :: Int32,
	_edgeVj :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Edge

data Side = Side {
	-- | Plane normal vector.
	_sideN :: Vec3 Double,
	-- | Distance from origin.
	_sideD :: Double
}
	deriving (Eq, Ord, Show)
makeLenses ''Side

data Texc = Texc {
	-- | Texture coordinates.
	_texcU :: Vec2 Double
}
	deriving (Eq, Ord, Show)
makeLenses ''Texc

data Offs = Offs {
	-- | Texcoords (Texc) index, side (plane) index, and vertex index, for this vertex.
	-- translation and scale.  Neverball sols ignore texture angle.
	_offsTi :: Int32,
	_offsSi :: Int32,
	-- | Vertex.
	_offsVi :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Offs

data Geom = Geom {
	-- | Material index (base texture).
	_geomMi :: Int32,
	-- | Triangle vertex 1 (Offs).
	_geomOi :: Int32,
	-- | Triangle vertex 2.
	_geomOj :: Int32,
	-- | Triangle vertex 3.
	_geomOk :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Geom

data Lump = Lump {
	-- | Lump flags.  In particular lumpFlagDetail == 1.
	_lumpFl :: Int32,
	_lumpV0 :: Int32,
	_lumpVc :: Int32,
	_lumpE0 :: Int32,
	_lumpEc :: Int32,
	_lumpG0 :: Int32,
	_lumpGc :: Int32,
	_lumpS0 :: Int32,
	_lumpSc :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Lump

-- | Three-part plane partitioning.
--
-- Normally the lumps are sorted while the nodes are being created.  Each
-- non-leaf node has 3 ranges in the sorted lumps, each range represented by a
-- Each node corresponds to 1) the range of all lumps in front of the plane
-- (ni), 2) all lumps on the plane (the node's direct l0 and lc range), and 3)
-- all lumps behind the plane (nj).
--
-- At 8 or so or fewer nodes, the base case is reached with a Nothing ni and nj
-- and si (represented by -1), and l0 and lc still represents the range of lumps.
--
-- Thus a node is like a node in a binary tree with its own unique range / span
-- of the lumps, and the nodes uniquely and comprehensively span the range of
-- lumps.
--
-- Now each body is a collection of lumps that follows the same path; it's like
-- a set of lumps.  Each body has a BSP partitioning with Nodes.
data Node = Node {
	_nodeSi :: Int32,
	_nodeNi :: Int32,
	_nodeNj :: Int32,
	_nodeL0 :: Int32,
	_nodeLc :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Node

data Path = Path {
	-- | Starting position.
	_pathP :: Vec3 Double,
	-- | Orientation (quaternion).
	_pathE :: Vec4 Double,
	-- | Travel time.
	_pathT :: Double,
	-- | Milliseconds.
	_pathTm :: Int32,

	-- | Next path.
	_pathPi :: Int32,
	-- | Enable flag.
	_pathF :: Int32,
	-- | Smooth flag.
	_pathS :: Int32,

	-- | Flags.
	_pathFl :: Int32,

	_pathP0 :: Int32,
	_pathP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Path

data Body = Body {
	-- | Translation path.
	_bodyP0 :: Int32,
	-- | Rotation path.
	_bodyP1 :: Int32,

	_bodyNi :: Int32,
	_bodyL0 :: Int32,
	_bodyLc :: Int32,
	_bodyG0 :: Int32,
	_bodyGc :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Body

data Item = Item {
	-- | Position.
	_itemP :: Vec3 Double,
	-- | Type.
	_itemT :: Int32,
	-- | Value.
	_itemN :: Int32,

	-- | Translation path.
	_itemP0 :: Int32,
	-- | Rotation path.
	_itemP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Item

data Goal = Goal {
	-- | Position.
	_goalP :: Vec3 Double,
	-- | Radius.
	_goalR :: Double,

	-- | Translation path.
	_goalP0 :: Int32,
	-- | Rotation path.
	_goalP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Goal

data Jump = Jump {
	-- | Position.
	_jumpP :: Vec3 Double,
	-- | Target position.
	_jumpQ :: Vec3 Double,
	-- | Radius.
	_jumpR :: Double,

	-- | Translation path.
	_jumpP0 :: Int32,
	-- | Rotation path.
	_jumpP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Jump

data Swch = Swch {
	-- | Position.
	_swchP :: Vec3 Double,
	-- | Radius.
	_swchR :: Double,
	-- | The activated path.
	_swchPi :: Int32,

	-- | Default timer.
	_swchT :: Double,
	-- | Milliseconds.
	_swchTm :: Int32,
	-- | Default state.
	_swchF :: Int32,
	-- | Is invisible?
	_swchI :: Int32,

	-- | Translation path.
	_swchP0 :: Int32,
	-- | Rotation path.
	_swchP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Swch

data Bill = Bill {
	_billFl :: Int32,
	_billMi :: Int32,
	-- | Repeat time interval.
	_billT :: Double,
	-- | Distance.
	_billD :: Double,

	-- | Width coefficients.
	_billW :: Vec3 Double,
	-- | Height coefficients.
	_billH :: Vec3 Double,

	-- | X rotation coefficients.
	_billRx :: Vec3 Double,
	-- | Y rotation coefficients.
	_billRy :: Vec3 Double,
	-- | Z rotation coefficients.
	_billRz :: Vec3 Double,

	_billP :: Vec3 Double,

	-- | Translation path.
	_billP0 :: Int32,
	-- | Rotation path.
	_billP1 :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Bill

data Ball = Ball {
	_ballP :: Vec3 Double,
	_ballR :: Double
}
	deriving (Eq, Ord, Show)
makeLenses ''Ball

data View = View {
	_viewP :: Vec3 Double,
	_viewQ :: Vec3 Double
}
	deriving (Eq, Ord, Show)
makeLenses ''View

data Dict = Dict {
	_dictAi :: Int32,
	_dictAj :: Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Dict

-- | The level format: .sol.
--
-- Uses little-endian format.
--
-- Also encoded as floats, but we read into doubles.
--
-- The Storable instance does not follow the sizeOf laws of not accessing its
-- argument, but is still useful for reading and writing when combined with
-- validation.  peekSol and pokeSol offer more pure implementations.
--
-- Note that Mtrl, Path, and Swch have irregular serializers, and Mtrl and Path
-- is variable-width.  The sizes by default are the conservative max sizes.
data Sol = Sol {
	_solMagic   :: Int32,
	_solVersion :: Int32,

	_solAc :: Int32,
	_solDc :: Int32,
	_solMc :: Int32,
	_solVc :: Int32,
	_solEc :: Int32,
	_solSc :: Int32,
	_solTc :: Int32,
	_solOc :: Int32,
	_solGc :: Int32,
	_solLc :: Int32,
	_solNc :: Int32,
	_solPc :: Int32,
	_solBc :: Int32,
	_solHc :: Int32,
	_solZc :: Int32,
	_solJc :: Int32,
	_solXc :: Int32,
	_solRc :: Int32,
	_solUc :: Int32,
	_solWc :: Int32,
	_solIc :: Int32,

	_solAv :: Array Int32 CChar,
	_solDv :: Array Int32 Dict,
	_solMv :: Array Int32 Mtrl,
	_solVv :: Array Int32 Vert,
	_solEv :: Array Int32 Edge,
	_solSv :: Array Int32 Side,
	_solTv :: Array Int32 Texc,
	_solOv :: Array Int32 Offs,
	_solGv :: Array Int32 Geom,
	_solLv :: Array Int32 Lump,
	_solNv :: Array Int32 Node,
	_solPv :: Array Int32 Path,
	_solBv :: Array Int32 Body,
	_solHv :: Array Int32 Item,
	_solZv :: Array Int32 Goal,
	_solJv :: Array Int32 Jump,
	_solXv :: Array Int32 Swch,
	_solRv :: Array Int32 Bill,
	_solUv :: Array Int32 Ball,
	_solWv :: Array Int32 View,
	_solIv :: Array Int32 Int32
}
	deriving (Eq, Ord, Show)
makeLenses ''Sol

type LevelIB = Sol

emptySol :: Sol
emptySol =
	(Sol
		solMagicConstant solVersionCurr
		0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
		a a a a a a a a a a a a a a a a a a a a a
	)
	where
		a :: (Storable a) => Array Int32 a
		a = IA.listArray (0, -1) []

-- * Optional low-level storable provisions (we don't use these)

instance Storable Sol where
	sizeOf = sizeOfEmptySol

	alignment
		~(Sol
			magic version
			ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
			--av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
			_  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
		) = max 1 . maximum $
			[
				alignment magic,
				alignment version,

				alignment ac,
				alignment dc,
				alignment mc,
				alignment vc,
				alignment ec,
				alignment sc,
				alignment tc,
				alignment oc,
				alignment gc,
				alignment lc,
				alignment nc,
				alignment pc,
				alignment bc,
				alignment hc,
				alignment zc,
				alignment jc,
				alignment xc,
				alignment rc,
				alignment uc,
				alignment wc,
				alignment ic,

				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: CChar),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Dict ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Mtrl ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Vert ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Edge ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Side ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Texc ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Offs ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Geom ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Lump ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Node ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Path ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Body ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Item ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Goal ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Jump ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Swch ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Bill ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Ball ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: View ),
				alignment (error "Internal error: alignment Sol: alignment accessed its argument!"  :: Int32)
			]

	peek = peekSol
	poke = pokeSol

sizeOfMtrl :: Mtrl -> Int
sizeOfMtrl = sizeOfMtrlMax

sizeOfMtrlMin :: Mtrl -> Int
sizeOfMtrlMin
	~(Mtrl
		_d _a _s _e _h _angle fl _f _alphaFunc _alphaRef
	) = sum $
		[
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x',
			--sizeOf x',
			sizeOf fl,
			solPathMax
			--sizeOf alphaFunc,
			--sizeOf x'
		]
	where x' = error "Internal error: sizeOf Mtrl: sizeOf accessed its argument!" :: Float

sizeOfMtrlMax :: Mtrl -> Int
sizeOfMtrlMax
	~(Mtrl
		_d _a _s _e _h _angle fl _f alphaFunc _alphaRef
	) = sum $
		[
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x',
			--sizeOf x',
			sizeOf fl,
			solPathMax,
			sizeOf alphaFunc,
			sizeOf x'
		]
	where x' = error "Internal error: sizeOf Mtrl: sizeOf accessed its argument!" :: Float

sizeOfExistingMtrl :: Mtrl -> Int
sizeOfExistingMtrl
	(Mtrl
		_d _a _s _e _h _angle fl _f alphaFunc _alphaRef
	) = sum $
		[
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x' * 4,
			sizeOf x',
			--sizeOf x',
			sizeOf fl,
			solPathMax,
			if' ((fl .&. mtrlFlagAlphaTest) /= 0) (sizeOf alphaFunc) 0,
			if' ((fl .&. mtrlFlagAlphaTest) /= 0) (sizeOf x'       ) 0
		]
	where x' = error "Internal error: sizeOf Mtrl: sizeOf accessed its argument!" :: Float

sizeOfPath :: Path -> Int
sizeOfPath = sizeOfPathMax

sizeOfPathMin :: Path -> Int
sizeOfPathMin
	~(Path
		_p _e _t _tm pi_ f s fl _p0 _p1
	) = sum $
		[
			3 * sizeOf x',
			--4 * sizeOf x',
			sizeOf x',
			--sizeOf tm,

			sizeOf pi_,
			sizeOf f,
			sizeOf s,

			sizeOf fl
			--sizeOf p0,
			--sizeOf p1
		]
	where x' = error "Internal error: sizeOf Path: sizeOf accessed its argument!" :: Float

sizeOfPathMax :: Path -> Int
sizeOfPathMax
	~(Path
		_p _e _t _tm pi_ f s fl p0 p1
	) = sum $
		[
			3 * sizeOf x',
			4 * sizeOf x',
			sizeOf x',
			--sizeOf tm,

			sizeOf pi_,
			sizeOf f,
			sizeOf s,

			sizeOf fl,
			sizeOf p0,
			sizeOf p1
		]
	where x' = error "Internal error: sizeOf Path: sizeOf accessed its argument!" :: Float

sizeOfExistingPath :: Path -> Int
sizeOfExistingPath
	(Path
		_p _e _t _tm pi_ f s fl p0 p1
	) = sum $
		[
			3 * sizeOf x',
			if' ((fl .&. pathFlagOriented) /= 0) (4 * sizeOf x') 0,
			sizeOf x',
			--sizeOf tm,

			sizeOf pi_,
			sizeOf f,
			sizeOf s,

			sizeOf fl,
			if' ((fl .&. pathFlagParented) /= 0) (sizeOf p0) 0,
			if' ((fl .&. pathFlagParented) /= 0) (sizeOf p1) 0
		]
	where x' = error "Internal error: sizeOf Path: sizeOf accessed its argument!" :: Float

sizeOfEmptySol :: Sol -> Int
sizeOfEmptySol
	~(Sol
		magic version
		ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
		--av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
		_  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
	) = sum $
		[
			sizeOf magic,
			sizeOf version,

			sizeOf ac,
			sizeOf dc,
			sizeOf mc,
			sizeOf vc,
			sizeOf ec,
			sizeOf sc,
			sizeOf tc,
			sizeOf oc,
			sizeOf gc,
			sizeOf lc,
			sizeOf nc,
			sizeOf pc,
			sizeOf bc,
			sizeOf hc,
			sizeOf zc,
			sizeOf jc,
			sizeOf xc,
			sizeOf rc,
			sizeOf uc,
			sizeOf wc,
			sizeOf ic,

			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: CChar),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Dict ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Mtrl ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Vert ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Edge ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Side ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Texc ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Offs ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Geom ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Lump ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Node ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Path ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Body ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Item ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Goal ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Jump ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Swch ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Bill ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Ball ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: View ),
			0 * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Int32)
		]

-- | Accesses its argument!
--
-- Disregards the actual array elements, making this suitable to parse lengths
-- and then judge needed total storage size.
--
-- Get a lower bound by the minimum size of variant width elements, but don't
-- precisely calculate the exact size.
sizeOfExistingSolMin :: Sol -> Int
sizeOfExistingSolMin
	(Sol
		magic version
		ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
		--av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
		_  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
	) = sum $
		[
			sizeOf magic,
			sizeOf version,

			sizeOf ac,
			sizeOf dc,
			sizeOf mc,
			sizeOf vc,
			sizeOf ec,
			sizeOf sc,
			sizeOf tc,
			sizeOf oc,
			sizeOf gc,
			sizeOf lc,
			sizeOf nc,
			sizeOf pc,
			sizeOf bc,
			sizeOf hc,
			sizeOf zc,
			sizeOf jc,
			sizeOf xc,
			sizeOf rc,
			sizeOf uc,
			sizeOf wc,
			sizeOf ic,

			fromIntegral ac * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: CChar),
			fromIntegral dc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Dict ),
			fromIntegral mc * sizeOfMtrlMin (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Mtrl ),
			fromIntegral vc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Vert ),
			fromIntegral ec * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Edge ),
			fromIntegral sc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Side ),
			fromIntegral tc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Texc ),
			fromIntegral oc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Offs ),
			fromIntegral gc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Geom ),
			fromIntegral lc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Lump ),
			fromIntegral nc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Node ),
			fromIntegral pc * sizeOfPathMin (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Path ),
			fromIntegral bc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Body ),
			fromIntegral hc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Item ),
			fromIntegral zc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Goal ),
			fromIntegral jc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Jump ),
			fromIntegral xc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Swch ),
			fromIntegral rc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Bill ),
			fromIntegral uc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Ball ),
			fromIntegral wc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: View ),
			fromIntegral ic * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Int32)
		]

-- | Accesses its argument!
--
-- Disregards the actual array elements, making this suitable to parse lengths
-- and then judge needed total storage size.
--
-- Get a lower bound by the maximum size of variant width elements, but don't
-- precisely calculate the exact size.
sizeOfExistingSolMax :: Sol -> Int
sizeOfExistingSolMax
	(Sol
		magic version
		ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
		--av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
		_  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _
	) = sum $
		[
			sizeOf magic,
			sizeOf version,

			sizeOf ac,
			sizeOf dc,
			sizeOf mc,
			sizeOf vc,
			sizeOf ec,
			sizeOf sc,
			sizeOf tc,
			sizeOf oc,
			sizeOf gc,
			sizeOf lc,
			sizeOf nc,
			sizeOf pc,
			sizeOf bc,
			sizeOf hc,
			sizeOf zc,
			sizeOf jc,
			sizeOf xc,
			sizeOf rc,
			sizeOf uc,
			sizeOf wc,
			sizeOf ic,

			fromIntegral ac * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: CChar),
			fromIntegral dc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Dict ),
			fromIntegral mc * sizeOfMtrlMax (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Mtrl ),
			fromIntegral vc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Vert ),
			fromIntegral ec * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Edge ),
			fromIntegral sc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Side ),
			fromIntegral tc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Texc ),
			fromIntegral oc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Offs ),
			fromIntegral gc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Geom ),
			fromIntegral lc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Lump ),
			fromIntegral nc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Node ),
			fromIntegral pc * sizeOfPathMax (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Path ),
			fromIntegral bc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Body ),
			fromIntegral hc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Item ),
			fromIntegral zc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Goal ),
			fromIntegral jc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Jump ),
			fromIntegral xc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Swch ),
			fromIntegral rc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Bill ),
			fromIntegral uc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Ball ),
			fromIntegral wc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: View ),
			fromIntegral ic * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Int32)
		]

-- | Accesses its argument!
--
-- Disregards the actual array elements, making this suitable to parse lengths
-- and then judge needed total storage size.
sizeOfExistingSol :: Sol -> Int
sizeOfExistingSol
	(Sol
		magic version
		ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
		--av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
		_  _  mv _  _  _  _  _  _  _  _  pv _  _  _  _  _  _  _  _  _
	) = sum $
		[
			sizeOf magic,
			sizeOf version,

			sizeOf ac,
			sizeOf dc,
			sizeOf mc,
			sizeOf vc,
			sizeOf ec,
			sizeOf sc,
			sizeOf tc,
			sizeOf oc,
			sizeOf gc,
			sizeOf lc,
			sizeOf nc,
			sizeOf pc,
			sizeOf bc,
			sizeOf hc,
			sizeOf zc,
			sizeOf jc,
			sizeOf xc,
			sizeOf rc,
			sizeOf uc,
			sizeOf wc,
			sizeOf ic,

			fromIntegral ac * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: CChar),
			fromIntegral dc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Dict ),
			sum . flip map (IA.elems mv) $ \mtrl -> sizeOfExistingMtrl mtrl,
			fromIntegral vc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Vert ),
			fromIntegral ec * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Edge ),
			fromIntegral sc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Side ),
			fromIntegral tc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Texc ),
			fromIntegral oc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Offs ),
			fromIntegral gc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Geom ),
			fromIntegral lc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Lump ),
			fromIntegral nc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Node ),
			sum . flip map (IA.elems pv) $ \path -> sizeOfExistingPath path,
			fromIntegral bc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Body ),
			fromIntegral hc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Item ),
			fromIntegral zc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Goal ),
			fromIntegral jc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Jump ),
			fromIntegral xc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Swch ),
			fromIntegral rc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Bill ),
			fromIntegral uc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Ball ),
			fromIntegral wc * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: View ),
			fromIntegral ic * sizeOf (error "Internal error: sizeOf Sol: sizeOf accessed its argument!"  :: Int32)
		]

peeki32Native :: Ptr () -> StateT Int IO Int32
peeki32Native ptr = do
	offset <- get
	val    <- lift $ peek (castPtr ptr `plusPtr` offset)
	put $ offset + sizeOf val
	return val

-- | Read a big-endian encoded int.
peeki32BE :: Ptr () -> StateT Int IO Int32
peeki32BE ptr = do
	offset <- get
	(byte0 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 0))
	(byte1 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 1))
	(byte2 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 2))
	(byte3 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 3))
	let (w32 :: Word32) = ((fromIntegral byte0 `shiftL` 24) .|. (fromIntegral byte1 `shiftL` 16) .|. (fromIntegral byte2 `shiftL` 8) .|. (fromIntegral byte3 `shiftL` 0))
	let (i32 :: Int32)  = fromIntegral w32
	let val = i32
	put $ offset + sizeOf w32
	return val

-- | Read a little-endian encoded int, which has backwards byte order (least
-- significant first).
peeki32LE :: Ptr () -> StateT Int IO Int32
peeki32LE ptr = do
	offset <- get
	(byte3 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 0))
	(byte2 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 1))
	(byte1 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 2))
	(byte0 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 3))
	let (w32 :: Word32) = ((fromIntegral byte0 `shiftL` 24) .|. (fromIntegral byte1 `shiftL` 16) .|. (fromIntegral byte2 `shiftL` 8) .|. (fromIntegral byte3 `shiftL` 0))
	let (i32 :: Int32)  = fromIntegral w32
	let val = i32
	put $ offset + sizeOf w32
	return val

-- | Read a little-endian encoded float as a double.
peekf32dLE :: Ptr () -> StateT Int IO Double
peekf32dLE ptr = do
	offset <- get
	(byte3 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 0))
	(byte2 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 1))
	(byte1 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 2))
	(byte0 :: Word8) <- lift $ peek (castPtr ptr `plusPtr` (offset + 3))
	let (w32 :: Word32) = ((fromIntegral byte0 `shiftL` 24) .|. (fromIntegral byte1 `shiftL` 16) .|. (fromIntegral byte2 `shiftL` 8) .|. (fromIntegral byte3 `shiftL` 0))

	-- GHC doesn't support coerce between Word32 and Float.
	-- Just malloc a new cfloat.
	{-
	let (f32 :: Float)  = coerce w32
	-}
	(cfloat :: ForeignPtr CFloat) <- lift $ mallocForeignPtr
	(cf32 :: CFloat) <- lift . withForeignPtr cfloat $ \cfloatPtr -> poke (castPtr cfloatPtr) w32 >> peek cfloatPtr
	let (f32 :: Float)  = coerce cf32

	let (d   :: Double) = realToFrac f32
	let val = d
	put $ offset + sizeOf w32
	return val

peekn :: forall a. (Storable a) => Ptr () -> Int32 -> StateT Int IO (Array Int32 a)
peekn ptr n
	| n <= 0    = lift $ newArray_ (0, (-1)) >>= freeze'
	| otherwise = do
		offset <- get
		let elemSizeof = error "Internal error: peekn: sizeOf accessed its element!"  :: a
		let sizeofElem = sizeOf elemSizeof
		array_ <- lift . (>>= freeze') . newGenArray (0, n-1) $ \idx ->
			peek (castPtr ptr `plusPtr` (offset + fromIntegral idx * sizeofElem))
		put $ offset + fromIntegral n * sizeofElem
		let _ = asType elemSizeof (array_ IA.! 0)
		return array_
	where
		freeze' :: IOArray Int32 a -> IO (Array Int32 a)
		freeze' = freeze

peeknWith :: forall a. (Storable a) => (a -> Int) -> Ptr () -> Int32 -> StateT Int IO (Array Int32 a)
peeknWith sizeOf_ ptr n
	| n <= 0    = lift $ newArray_ (0, (-1)) >>= freeze'
	| otherwise = do
		offset <- get
		(elems_, dynSize) <- lift . flip fix (0, 0) $ \me (idx, relOffset) -> do
			if' (idx >= n) (return ([], relOffset)) $ do
			elem_ <- peek (castPtr ptr `plusPtr` (offset + relOffset))
			first (elem_:) <$> me (idx+1, relOffset + sizeOf_ elem_)
		let array_ = IA.listArray (0, n-1) $ elems_
		put $ offset + dynSize
		return array_
	where
		freeze' :: IOArray Int32 a -> IO (Array Int32 a)
		freeze' = freeze

peekCString :: Ptr () -> Int -> StateT Int IO String
peekCString ptr' bufSize = do
	offset <- get
	str <- flip fix 0 $ \withRelOffset relOffset -> do
		if' (relOffset >= bufSize) (return []) $ do
		(c :: Word8) <- lift $ peek (castPtr ptr' `plusPtr` (offset + relOffset))
		if' (c == 0) (return []) $ do
		(asciiChar c:) <$> withRelOffset (relOffset+1)
	put $ offset + bufSize
	return str
	where
		asciiChar :: Word8 -> Char
		asciiChar = toEnum . fromEnum

asType :: a -> a -> ()
asType _ _ = ()

-- mfix doesn't seem quite up to the task; it throws a blocked indefinitely in
-- mvar exception.  Just do it manually.
{-
peekSol :: Ptr Sol -> IO Sol
peekSol ptr = mfix $ \sol -> flip evalStateT 0 $ Sol <$>
	peeki32LE ptr' <*>  -- magic
	peeki32LE ptr' <*>  -- version

	peeki32LE ptr' <*>  -- ac
	peeki32LE ptr' <*>  -- dc
	peeki32LE ptr' <*>  -- mc
	peeki32LE ptr' <*>  -- vc
	peeki32LE ptr' <*>  -- ec
	peeki32LE ptr' <*>  -- sc
	peeki32LE ptr' <*>  -- tc
	peeki32LE ptr' <*>  -- oc
	peeki32LE ptr' <*>  -- gc
	peeki32LE ptr' <*>  -- lc
	peeki32LE ptr' <*>  -- nc
	peeki32LE ptr' <*>  -- pc
	peeki32LE ptr' <*>  -- bc
	peeki32LE ptr' <*>  -- hc
	peeki32LE ptr' <*>  -- zc
	peeki32LE ptr' <*>  -- jc
	peeki32LE ptr' <*>  -- xc
	peeki32LE ptr' <*>  -- rc
	peeki32LE ptr' <*>  -- uc
	peeki32LE ptr' <*>  -- wc
	peeki32LE ptr' <*>  -- ic

	peekn ptr' (sol^.solAc) <*>  -- av
	peekn ptr' (sol^.solDc) <*>  -- dv
	peeknWith sizeOfExistingMtrl ptr' (sol^.solMc) <*>  -- mv
	peekn ptr' (sol^.solVc) <*>  -- vv
	peekn ptr' (sol^.solEc) <*>  -- ev
	peekn ptr' (sol^.solSc) <*>  -- sv
	peekn ptr' (sol^.solTc) <*>  -- tv
	peekn ptr' (sol^.solOc) <*>  -- ov
	peekn ptr' (sol^.solGc) <*>  -- gv
	peekn ptr' (sol^.solLc) <*>  -- lv
	peekn ptr' (sol^.solNc) <*>  -- nv
	peeknWith sizeOfExistingPath ptr' (sol^.solPc) <*>  -- pv
	peekn ptr' (sol^.solBc) <*>  -- bv
	peekn ptr' (sol^.solHc) <*>  -- hv
	peekn ptr' (sol^.solZc) <*>  -- zv
	peekn ptr' (sol^.solJc) <*>  -- jv
	peekn ptr' (sol^.solXc) <*>  -- xv
	peekn ptr' (sol^.solRc) <*>  -- rv
	peekn ptr' (sol^.solUc) <*>  -- uv
	peekn ptr' (sol^.solWc) <*>  -- wv
	peekn ptr' (sol^.solIc)      -- iv

	where ptr' = castPtr ptr
-}
peekSol :: Ptr Sol -> IO Sol
peekSol ptr = flip evalStateT 0 $ do
	magic   <- peeki32LE ptr'
	version <- peeki32LE ptr'

	ac <- peeki32LE ptr'
	dc <- peeki32LE ptr'
	mc <- peeki32LE ptr'
	vc <- peeki32LE ptr'
	ec <- peeki32LE ptr'
	sc <- peeki32LE ptr'
	tc <- peeki32LE ptr'
	oc <- peeki32LE ptr'
	gc <- peeki32LE ptr'
	lc <- peeki32LE ptr'
	nc <- peeki32LE ptr'
	pc <- peeki32LE ptr'
	bc <- peeki32LE ptr'
	hc <- peeki32LE ptr'
	zc <- peeki32LE ptr'
	jc <- peeki32LE ptr'
	xc <- peeki32LE ptr'
	rc <- peeki32LE ptr'
	uc <- peeki32LE ptr'
	wc <- peeki32LE ptr'
	ic <- peeki32LE ptr'

	av <- peekn ptr' ac
	dv <- peekn ptr' dc
	mv <- peeknWith sizeOfExistingMtrl ptr' mc
	vv <- peekn ptr' vc
	ev <- peekn ptr' ec
	sv <- peekn ptr' sc
	tv <- peekn ptr' tc
	ov <- peekn ptr' oc
	gv <- peekn ptr' gc
	lv <- peekn ptr' lc
	nv <- peekn ptr' nc
	pv <- peeknWith sizeOfExistingPath ptr' pc
	bv <- peekn ptr' bc
	hv <- peekn ptr' hc
	zv <- peekn ptr' zc
	jv <- peekn ptr' jc
	xv <- peekn ptr' xc
	rv <- peekn ptr' rc
	uv <- peekn ptr' uc
	wv <- peekn ptr' wc
	iv <- peekn ptr' ic

	return $ Sol {
		_solMagic   = magic,
		_solVersion = version,

		_solAc = ac,
		_solDc = dc,
		_solMc = mc,
		_solVc = vc,
		_solEc = ec,
		_solSc = sc,
		_solTc = tc,
		_solOc = oc,
		_solGc = gc,
		_solLc = lc,
		_solNc = nc,
		_solPc = pc,
		_solBc = bc,
		_solHc = hc,
		_solZc = zc,
		_solJc = jc,
		_solXc = xc,
		_solRc = rc,
		_solUc = uc,
		_solWc = wc,
		_solIc = ic,

		_solAv = av,
		_solDv = dv,
		_solMv = mv,
		_solVv = vv,
		_solEv = ev,
		_solSv = sv,
		_solTv = tv,
		_solOv = ov,
		_solGv = gv,
		_solLv = lv,
		_solNv = nv,
		_solPv = pv,
		_solBv = bv,
		_solHv = hv,
		_solZv = zv,
		_solJv = jv,
		_solXv = xv,
		_solRv = rv,
		_solUv = uv,
		_solWv = wv,
		_solIv = iv
	}

	where ptr' = castPtr ptr

-- | Parse only the lengths, leaving empty arrays.
--
-- This can be used to safely read in a whole SOL file with arbitrary input.
peekSolLengths :: Ptr Sol -> IO Sol
peekSolLengths ptr = flip evalStateT 0 $ Sol <$>
	peeki32LE ptr' <*>  -- magic
	peeki32LE ptr' <*>  -- version

	peeki32LE ptr' <*>  -- ac
	peeki32LE ptr' <*>  -- dc
	peeki32LE ptr' <*>  -- mc
	peeki32LE ptr' <*>  -- vc
	peeki32LE ptr' <*>  -- ec
	peeki32LE ptr' <*>  -- sc
	peeki32LE ptr' <*>  -- tc
	peeki32LE ptr' <*>  -- oc
	peeki32LE ptr' <*>  -- gc
	peeki32LE ptr' <*>  -- lc
	peeki32LE ptr' <*>  -- nc
	peeki32LE ptr' <*>  -- pc
	peeki32LE ptr' <*>  -- bc
	peeki32LE ptr' <*>  -- hc
	peeki32LE ptr' <*>  -- zc
	peeki32LE ptr' <*>  -- jc
	peeki32LE ptr' <*>  -- xc
	peeki32LE ptr' <*>  -- rc
	peeki32LE ptr' <*>  -- uc
	peeki32LE ptr' <*>  -- wc
	peeki32LE ptr' <*>  -- ic

	pure emptyArray <*>  -- av
	pure emptyArray <*>  -- dv
	pure emptyArray <*>  -- mv
	pure emptyArray <*>  -- vv
	pure emptyArray <*>  -- ev
	pure emptyArray <*>  -- sv
	pure emptyArray <*>  -- tv
	pure emptyArray <*>  -- ov
	pure emptyArray <*>  -- gv
	pure emptyArray <*>  -- lv
	pure emptyArray <*>  -- nv
	pure emptyArray <*>  -- pv
	pure emptyArray <*>  -- bv
	pure emptyArray <*>  -- hv
	pure emptyArray <*>  -- zv
	pure emptyArray <*>  -- jv
	pure emptyArray <*>  -- xv
	pure emptyArray <*>  -- rv
	pure emptyArray <*>  -- uv
	pure emptyArray <*>  -- wv
	pure emptyArray      -- iv

	where
		ptr' = castPtr ptr
		a :: (Storable a) => Array Int32 a
		a = IA.listArray (0, -1) []
		emptyArray :: (Storable a) => Array Int32 a
		emptyArray = a

pokeSol :: Ptr Sol -> Sol -> IO ()
pokeSol ptr
	(Sol
		magic version
		ac dc mc vc ec sc tc oc gc lc nc pc bc hc zc jc xc rc uc wc ic
		av dv mv vv ev sv tv ov gv lv nv pv bv hv zv jv xv rv uv wv iv
	) = flip evalStateT 0 $ do
		pokei32LE ptr' magic
		pokei32LE ptr' version

		pokei32LE ptr' ac
		pokei32LE ptr' dc
		pokei32LE ptr' mc
		pokei32LE ptr' vc
		pokei32LE ptr' ec
		pokei32LE ptr' sc
		pokei32LE ptr' tc
		pokei32LE ptr' oc
		pokei32LE ptr' gc
		pokei32LE ptr' lc
		pokei32LE ptr' nc
		pokei32LE ptr' pc
		pokei32LE ptr' bc
		pokei32LE ptr' hc
		pokei32LE ptr' zc
		pokei32LE ptr' jc
		pokei32LE ptr' xc
		pokei32LE ptr' rc
		pokei32LE ptr' uc
		pokei32LE ptr' wc
		pokei32LE ptr' ic

		poken ptr' ac av
		poken ptr' dc dv
		pokenWith sizeOfExistingMtrl ptr' mc mv
		poken ptr' vc vv
		poken ptr' ec ev
		poken ptr' sc sv
		poken ptr' tc tv
		poken ptr' oc ov
		poken ptr' gc gv
		poken ptr' lc lv
		poken ptr' nc nv
		pokenWith sizeOfExistingPath ptr' pc pv
		poken ptr' bc bv
		poken ptr' hc hv
		poken ptr' zc zv
		poken ptr' jc jv
		poken ptr' xc xv
		poken ptr' rc rv
		poken ptr' uc uv
		poken ptr' wc wv
		poken ptr' ic iv

	where ptr' = castPtr ptr

pokei32Native :: Ptr () -> Int32 -> StateT Int IO ()
pokei32Native ptr val = do
	offset <- get
	lift $ poke (castPtr ptr `plusPtr` offset) val
	put $ offset + sizeOf val

pokei32BE :: Ptr () -> Int32 -> StateT Int IO ()
pokei32BE ptr val = do
	offset <- get
	let (w32 :: Word32) = fromIntegral val
	let (byte0 :: Word8) = fromIntegral $ (w32 .&. 0xFF000000) `shiftR` 24
	let (byte1 :: Word8) = fromIntegral $ (w32 .&. 0x00FF0000) `shiftR` 16
	let (byte2 :: Word8) = fromIntegral $ (w32 .&. 0x0000FF00) `shiftR`  8
	let (byte3 :: Word8) = fromIntegral $ (w32 .&. 0x000000FF) `shiftR`  0
	lift $ poke (castPtr ptr `plusPtr` (offset + 0)) byte0
	lift $ poke (castPtr ptr `plusPtr` (offset + 1)) byte1
	lift $ poke (castPtr ptr `plusPtr` (offset + 2)) byte2
	lift $ poke (castPtr ptr `plusPtr` (offset + 3)) byte3
	put $ offset + sizeOf w32

pokei32LE :: Ptr () -> Int32 -> StateT Int IO ()
pokei32LE ptr val = do
	offset <- get
	let (w32 :: Word32) = fromIntegral val
	let (byte3 :: Word8) = fromIntegral $ (w32 .&. 0xFF000000) `shiftR` 24
	let (byte2 :: Word8) = fromIntegral $ (w32 .&. 0x00FF0000) `shiftR` 16
	let (byte1 :: Word8) = fromIntegral $ (w32 .&. 0x0000FF00) `shiftR`  8
	let (byte0 :: Word8) = fromIntegral $ (w32 .&. 0x000000FF) `shiftR`  0
	lift $ poke (castPtr ptr `plusPtr` (offset + 0)) byte0
	lift $ poke (castPtr ptr `plusPtr` (offset + 1)) byte1
	lift $ poke (castPtr ptr `plusPtr` (offset + 2)) byte2
	lift $ poke (castPtr ptr `plusPtr` (offset + 3)) byte3
	put $ offset + sizeOf w32

pokef32dLE :: Ptr () -> Double -> StateT Int IO ()
pokef32dLE ptr val = do
	offset <- get
	let (fl :: Float) = realToFrac val

	-- GHC doesn't support coerce between Float and Word32.
	-- Just malloc a new word32.
	{-
	let (w32 :: Word32) = coerce f
	-}
	(w32Ptr :: ForeignPtr Word32) <- lift $ mallocForeignPtr
	(cw32 :: Word32) <- lift . withForeignPtr w32Ptr $ \ptr_ -> poke (castPtr ptr_) fl >> peek ptr_
	let (w32 :: Word32) = coerce cw32

	let (byte3 :: Word8) = fromIntegral $ (w32 .&. 0xFF000000) `shiftR` 24
	let (byte2 :: Word8) = fromIntegral $ (w32 .&. 0x00FF0000) `shiftR` 16
	let (byte1 :: Word8) = fromIntegral $ (w32 .&. 0x0000FF00) `shiftR`  8
	let (byte0 :: Word8) = fromIntegral $ (w32 .&. 0x000000FF) `shiftR`  0
	lift $ poke (castPtr ptr `plusPtr` (offset + 0)) byte0
	lift $ poke (castPtr ptr `plusPtr` (offset + 1)) byte1
	lift $ poke (castPtr ptr `plusPtr` (offset + 2)) byte2
	lift $ poke (castPtr ptr `plusPtr` (offset + 3)) byte3
	put $ offset + sizeOf w32

poken :: forall a. (Storable a) => Ptr () -> Int32 -> Array Int32 a -> StateT Int IO ()
poken ptr n array_
	| n <= 0    = return ()
	| otherwise = do
		let elemSizeof = error "Internal error: poken: sizeOf accessed its element!"  :: a
		let sizeofElem = sizeOf elemSizeof
		forM_ array_ $ \elem_ -> do
			offset <- get
			lift $ poke (castPtr ptr `plusPtr` offset) elem_
			put $ offset + sizeofElem

pokenWith :: forall a. (Storable a) => (a -> Int) -> Ptr () -> Int32 -> Array Int32 a -> StateT Int IO ()
pokenWith sizeOf_ ptr n array_
	| n <= 0    = return ()
	| otherwise = do
		forM_ array_ $ \elem_ -> do
			offset <- get
			lift $ poke (castPtr ptr `plusPtr` offset) elem_
			put $ offset + sizeOf_ elem_

pokeCString :: Ptr () -> Int -> String -> StateT Int IO ()
pokeCString ptr bufSize str
	| bufSize <= 0 = return ()
	| otherwise = do
		offset <- get
		let strLen = max (bufSize - 1) $ length str
		forM_ (zip [0..] (take strLen $ str)) $ \(idx, c) -> do
			lift $ poke (castPtr ptr `plusPtr` (offset + idx)) $ truncateChar c
		lift $ poke (castPtr ptr `plusPtr` (offset + (strLen - 1))) (0x00 :: Word8)
		put $ offset + bufSize
	where
		truncateChar :: Char -> Word8
		truncateChar = toEnum . fromEnum

instance Storable Mtrl where
	sizeOf = sizeOfMtrlMax
	alignment
		~(Mtrl
			_d _a _s _e h _angle fl _f alphaFunc _alphaRef
		) = max 1 . maximum $
			[
				alignment x',
				alignment x',
				alignment x',
				alignment x',
				alignment h,
				--alignment angle,
				alignment fl,
				1,
				alignment alphaFunc,
				alignment x'
			]
		where x' = error "Internal error: alignment Mtrl: alignment accessed its argument!" :: Float

	-- Irregular encoding; replace straightforward.
	{-
	peek ptr = flip evalStateT 0 $ Mtrl <$>
		(Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- mtrlD
		(Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- mtrlA
		(Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- mtrlS
		(Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- mtrlE
		peekf32dLE ptr' <*>  -- mtrlH

		peekf32dLE ptr' <*>  -- mtrlAngle

		peeki32LE ptr' <*>  -- mtrlFl

		peekCString ptr' solPathMax <*>  -- mtrlF

		peeki32LE ptr' <*>  -- mtrlAlphaFunc
		peekf32dLE ptr'  -- mtrlAlphaRef

		where ptr' = castPtr ptr

	poke ptr
		(Mtrl
			d a s e h angle fl f alphaFunc alphaRef
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' (d^.x4) >> pokef32dLE ptr' (d^.y4) >> pokef32dLE ptr' (d^.z4) >> pokef32dLE ptr' (d^.w4)
			pokef32dLE ptr' (a^.x4) >> pokef32dLE ptr' (a^.y4) >> pokef32dLE ptr' (a^.z4) >> pokef32dLE ptr' (a^.w4)
			pokef32dLE ptr' (s^.x4) >> pokef32dLE ptr' (s^.y4) >> pokef32dLE ptr' (s^.z4) >> pokef32dLE ptr' (s^.w4)
			pokef32dLE ptr' (e^.x4) >> pokef32dLE ptr' (e^.y4) >> pokef32dLE ptr' (e^.z4) >> pokef32dLE ptr' (e^.w4)
			pokef32dLE ptr' h

			pokef32dLE ptr' angle

			pokei32LE ptr' fl

			pokeCString ptr' solPathMax f

			pokei32LE ptr' alphaFunc
			pokef32dLE ptr' alphaRef

		where ptr' = castPtr ptr
	-}

	peek ptr = flip evalStateT 0 $ do
		d <- Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr'
		a <- Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr'
		s <- Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr'
		e <- Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr'
		h <- peekf32dLE ptr'

		let angle = 0.0

		fl <- peeki32LE ptr'

		f <- peekCString ptr' solPathMax

		alphaFunc <- if' ((fl .&. mtrlFlagAlphaTest) /= 0) (peeki32LE  ptr') (pure 0  )
		alphaRef  <- if' ((fl .&. mtrlFlagAlphaTest) /= 0) (peekf32dLE ptr') (pure 0.0)

		return $ Mtrl {
			_mtrlD = d,
			_mtrlA = a,
			_mtrlS = s,
			_mtrlE = e,
			_mtrlH = h,

			_mtrlAngle = angle,

			_mtrlFl = fl,
			_mtrlF  = f,

			_mtrlAlphaFunc = alphaFunc,
			_mtrlAlphaRef  = alphaRef
		}

		where ptr' = castPtr ptr

	poke ptr
		(Mtrl
			d a s e h _angle fl f alphaFunc alphaRef
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' (d^.x4) >> pokef32dLE ptr' (d^.y4) >> pokef32dLE ptr' (d^.z4) >> pokef32dLE ptr' (d^.w4)
			pokef32dLE ptr' (a^.x4) >> pokef32dLE ptr' (a^.y4) >> pokef32dLE ptr' (a^.z4) >> pokef32dLE ptr' (a^.w4)
			pokef32dLE ptr' (s^.x4) >> pokef32dLE ptr' (s^.y4) >> pokef32dLE ptr' (s^.z4) >> pokef32dLE ptr' (s^.w4)
			pokef32dLE ptr' (e^.x4) >> pokef32dLE ptr' (e^.y4) >> pokef32dLE ptr' (e^.z4) >> pokef32dLE ptr' (e^.w4)
			pokef32dLE ptr' h

			--pokef32dLE ptr' angle

			pokei32LE ptr' fl

			pokeCString ptr' solPathMax f

			when ((fl .&. mtrlFlagAlphaTest) /= 0) $ pokei32LE  ptr' alphaFunc
			when ((fl .&. mtrlFlagAlphaTest) /= 0) $ pokef32dLE ptr' alphaRef

		where ptr' = castPtr ptr

instance Storable Vert where
	sizeOf    ~(Vert (Vec3 _ _ _)) = sum [3 * sizeOf x']
		where x' = error "Internal error: sizeOf Vert: sizeOf accessed its argument!" :: Float
	alignment ~(Vert (Vec3 _ _ _)) = max 1 $ maximum [alignment x']
		where x' = error "Internal error: alignment Vert: alignment accessed its argument!" :: Float
	peek ptr = flip evalStateT 0 $ Vert <$> (Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr')
		where ptr' = castPtr ptr
	poke ptr (Vert (Vec3 x y z)) = flip evalStateT 0 $ pokef32dLE ptr' x >> pokef32dLE ptr' y >> pokef32dLE ptr' z
		where ptr' = castPtr ptr

instance Storable Edge where
	sizeOf    ~(Edge vi vj) = sum [sizeOf vi, sizeOf vj]
	alignment ~(Edge vi vj) = max 1 $ maximum [alignment vi, alignment vj]
	peek ptr = flip evalStateT 0 $ Edge <$> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Edge vi vj) = flip evalStateT 0 $ pokei32LE ptr' vi >> pokei32LE ptr' vj
		where ptr' = castPtr ptr

instance Storable Side where
	sizeOf    ~(Side (Vec3 _nx _ny _nz) _d) = sum [3 * sizeOf x', sizeOf x']
		where x' = error "Internal error: sizeOf Side: sizeOf accessed its argument!" :: Float
	alignment ~(Side (Vec3 _nx _ny _nz) _d) = max 1 $ maximum [alignment x', alignment x']
		where x' = error "Internal error: alignment Side: alignment accessed its argument!" :: Float
	peek ptr = flip evalStateT 0 $ Side <$> (Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*> peekf32dLE ptr'
		where ptr' = castPtr ptr
	poke ptr (Side (Vec3 nx ny nz) d) = flip evalStateT 0 $ pokef32dLE ptr' nx >> pokef32dLE ptr' ny >> pokef32dLE ptr' nz >> pokef32dLE ptr' d
		where ptr' = castPtr ptr

instance Storable Texc where
	sizeOf    ~(Texc (Vec2 _tx _ty)) = sum [2 * sizeOf x']
		where x' = error "Internal error: sizeOf Texc: sizeOf accessed its argument!" :: Float
	alignment ~(Texc (Vec2 _tx _ty)) = max 1 $ maximum [alignment x', alignment x']
		where x' = error "Internal error: alignment Texc: alignment accessed its argument!" :: Float
	peek ptr = flip evalStateT 0 $ Texc <$> (Vec2 <$> peekf32dLE ptr' <*> peekf32dLE ptr')
		where ptr' = castPtr ptr
	poke ptr (Texc (Vec2 tx ty)) = flip evalStateT 0 $ pokef32dLE ptr' tx >> pokef32dLE ptr' ty
		where ptr' = castPtr ptr

instance Storable Offs where
	sizeOf    ~(Offs ti si vi) = sum [sizeOf ti, sizeOf si, sizeOf vi]
	alignment ~(Offs ti si vi) = max 1 $ maximum [alignment ti, alignment si, alignment vi]
	peek ptr = flip evalStateT 0 $ Offs <$> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Offs ti si vi) = flip evalStateT 0 $ pokei32LE ptr' ti >> pokei32LE ptr' si >> pokei32Native ptr' vi
		where ptr' = castPtr ptr

instance Storable Geom where
	sizeOf    ~(Geom mi oi oj ok) = sum [sizeOf mi, sizeOf oi, sizeOf oj, sizeOf ok]
	alignment ~(Geom mi oi oj ok) = max 1 $ maximum [alignment mi, alignment oi, alignment oj, alignment ok]
	peek ptr = flip evalStateT 0 $ Geom <$> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Geom mi oi oj ok) = flip evalStateT 0 $ pokei32LE ptr' mi >> pokei32LE ptr' oi >> pokei32Native ptr' oj >> pokei32LE ptr' ok
		where ptr' = castPtr ptr

instance Storable Lump where
	sizeOf    ~(Lump fl v0 vc e0 ec g0 gc s0 sc) = sum [sizeOf fl, sizeOf v0, sizeOf vc, sizeOf e0, sizeOf ec, sizeOf g0, sizeOf gc, sizeOf s0, sizeOf sc]
	alignment ~(Lump fl v0 vc e0 ec g0 gc s0 sc) = max 1 $ maximum [alignment fl, alignment v0, alignment vc, alignment e0, alignment ec, alignment g0, alignment gc, alignment s0, alignment sc]
	peek ptr = flip evalStateT 0 $ Lump <$> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Lump fl v0 vc e0 ec g0 gc s0 sc) = flip evalStateT 0 $ pokei32LE ptr' fl >> pokei32LE ptr' v0 >> pokei32LE ptr' vc >> pokei32LE ptr' e0 >> pokei32LE ptr' ec >> pokei32LE ptr' g0 >> pokei32LE ptr' gc >> pokei32LE ptr' s0 >> pokei32LE ptr' sc
		where ptr' = castPtr ptr

instance Storable Node where
	sizeOf    ~(Node si ni nj l0 lc) = sum [sizeOf si, sizeOf ni, sizeOf nj, sizeOf l0, sizeOf lc]
	alignment ~(Node si ni nj l0 lc) = max 1 $ maximum [alignment si, alignment ni, alignment nj, alignment l0, alignment lc]
	peek ptr = flip evalStateT 0 $ Node <$> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Node si ni nj l0 lc) = flip evalStateT 0 $ pokei32LE ptr' si >> pokei32LE ptr' ni >> pokei32LE ptr' nj >> pokei32LE ptr' l0 >> pokei32LE ptr' lc
		where ptr' = castPtr ptr

instance Storable Path where
	sizeOf = sizeOfPathMax
	alignment
		~(Path
			_p _e _t _tm pi_ f s fl p0 p1
		) = max 1 . maximum $
			[
				alignment x',
				alignment x',
				alignment x',
				--alignment tm,

				alignment pi_,
				alignment f,
				alignment s,

				alignment fl,
				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Path: alignment accessed its argument!" :: Float

	{-
	peek ptr = flip evalStateT 0 $ Path <$>
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p
		(Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- e
		peekf32dLE ptr' <*>  -- t
		peeki32LE ptr' <*>  -- tm

		peeki32LE ptr' <*>  -- pi
		peeki32LE ptr' <*>  -- f
		peeki32LE ptr' <*>  -- s

		peeki32LE ptr' <*>  -- fl

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'  -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Path
			(Vec3 px py pz) (Vec4 ex ey ez ew) t tm pi_ f s fl p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokef32dLE ptr' ex >> pokef32dLE ptr' ey >> pokef32dLE ptr' ez >> pokef32dLE ptr' ew
			pokef32dLE ptr' t
			pokei32LE ptr' tm

			pokei32LE ptr' pi_
			pokei32LE ptr' f
			pokei32LE ptr' s

			pokei32LE ptr' fl

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr
	-}
	-- Actually, Paths are serialized in a different order, and are also
	-- variable width.  So we'll need to provide special implementations
	-- carefully.
	peek ptr = flip evalStateT 0 $ do
		p   <- Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr'
		t   <- peekf32dLE ptr'
		pi_ <- peeki32LE ptr'
		f   <- peeki32LE ptr'
		s   <- peeki32LE ptr'
		fl  <- peeki32LE ptr'

		e        <- if' ((fl .&. pathFlagOriented) /= 0) (Vec4 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') (return $ Vec4 0.0 0.0 0.0 0.0)
		(p0, p1) <- if' ((fl .&. pathFlagParented) /= 0) ((,)  <$> peeki32LE  ptr' <*> peeki32LE  ptr'                                        ) (return $ (0, 0))

		let tm = round $ (1000.0*t)

		let p1' = if' (p1 < 0) p0 p1

		return $ Path {
			_pathP  = p,
			_pathE  = e,
			_pathT  = t,
			_pathTm = tm,

			_pathPi = pi_,
			_pathF  = f,
			_pathS  = s,

			_pathFl = fl,

			_pathP0 = p0,
			_pathP1 = p1'
		}

		where ptr' = castPtr ptr
	poke ptr
		(Path
			(Vec3 px py pz) (Vec4 ex ey ez ew) t _tm pi_ f s fl p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokef32dLE ptr' t
			pokei32LE  ptr' pi_
			pokei32LE  ptr' f
			pokei32LE  ptr' s
			pokei32LE  ptr' fl

			when ((fl .&. pathFlagOriented) /= 0) (pokef32dLE ptr' ex >> pokef32dLE ptr' ey >> pokef32dLE ptr' ez >> pokef32dLE ptr' ew)
			when ((fl .&. pathFlagParented) /= 0) (pokei32LE ptr' p0 >> pokei32LE ptr' p1)

		where ptr' = castPtr ptr

instance Storable Body where
	sizeOf    ~(Body p0 p1 ni l0 lc g0 gc) = sum [sizeOf p0, sizeOf p1, sizeOf ni, sizeOf l0, sizeOf lc, sizeOf g0, sizeOf gc]
	alignment ~(Body p0 p1 ni l0 lc g0 gc) = max 1 $ maximum [alignment p0, alignment p1, alignment ni, alignment l0, alignment lc, alignment g0, alignment gc]
	peek ptr = flip evalStateT 0 $ Body <$> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Body p0 p1 ni l0 lc g0 gc) = flip evalStateT 0 $ pokei32LE ptr' p0 >> pokei32LE ptr' p1 >> pokei32Native ptr' ni >> pokei32LE ptr' l0 >> pokei32LE ptr' lc >> pokei32Native ptr' g0 >> pokei32LE ptr' gc
		where ptr' = castPtr ptr

instance Storable Item where
	sizeOf
		~(Item
			_p t n p0 p1
		) = sum $
			[
				3 * sizeOf x',
				sizeOf t,
				sizeOf n,

				sizeOf p0,
				sizeOf p1
			]
		where x' = error "Internal error: sizeOf Item: sizeOf accessed its argument!" :: Float
	alignment
		~(Item
			_p t n p0 p1
		) = max 1 . maximum $
			[
				alignment x',
				alignment t,
				alignment n,

				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Item: alignment accessed its argument!" :: Float

	peek ptr = ((\item -> item & (itemP1 .~ (if' (item^.itemP1 < 0) (item^.itemP0) (item^.itemP1)))) <$>) . flip evalStateT 0 $ Item <$>
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p
		peeki32LE ptr' <*>  -- t
		peeki32LE ptr' <*>  -- n

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'      -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Item
			(Vec3 px py pz) t n p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokei32LE ptr' t
			pokei32LE ptr' n

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr

instance Storable Goal where
	sizeOf
		~(Goal
			_p _r p0 p1
		) = sum $
			[
				3 * sizeOf x',
				sizeOf x',

				sizeOf p0,
				sizeOf p1
			]
		where x' = error "Internal error: sizeOf Item: sizeOf accessed its argument!" :: Float
	alignment
		~(Goal
			_p _r p0 p1
		) = max 1 . maximum $
			[
				alignment x',
				alignment x',

				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Item: alignment accessed its argument!" :: Float

	peek ptr = ((\goal -> goal & (goalP1 .~ (if' (goal^.goalP1 < 0) (goal^.goalP0) (goal^.goalP1)))) <$>) . flip evalStateT 0 $ Goal <$>
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p
		peekf32dLE ptr' <*>  -- r

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'      -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Goal
			(Vec3 px py pz) r p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokef32dLE ptr' r

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr

instance Storable Jump where
	sizeOf
		~(Jump
			_p _q _r p0 p1
		) = sum $
			[
				3 * sizeOf x',
				3 * sizeOf x',
				sizeOf x',

				sizeOf p0,
				sizeOf p1
			]
		where x' = error "Internal error: sizeOf Jump: sizeOf accessed its argument!" :: Float
	alignment
		~(Jump
			_p _q _r p0 p1
		) = max 1 . maximum $
			[
				alignment x',
				alignment x',
				alignment x',

				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Jump: alignment accessed its argument!" :: Float

	peek ptr = ((\jump -> jump & (jumpP1 .~ (if' (jump^.jumpP1 < 0) (jump^.jumpP0) (jump^.jumpP1)))) <$>) . flip evalStateT 0 $ Jump <$>
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- q
		peekf32dLE ptr' <*>  -- r

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'      -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Jump
			(Vec3 px py pz) (Vec3 qx qy qz) r p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokef32dLE ptr' qx >> pokef32dLE ptr' qy >> pokef32dLE ptr' qz
			pokef32dLE ptr' r

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr

instance Storable Swch where
	sizeOf
		~(Swch
			_p _r pi_ _t _tm f i p0 p1
		) = sum $
			[
				3 * sizeOf x',
				sizeOf x',
				sizeOf pi_,
				sizeOf x', sizeOf x',
				--sizeOf tm,
				sizeOf f, sizeOf f,
				sizeOf i,

				sizeOf p0,
				sizeOf p1
			]
		where x' = error "Internal error: sizeOf Swch: sizeOf accessed its argument!" :: Float
	alignment
		~(Swch
			_p _r pi_ _t _tm f i p0 p1
		) = max 1 . maximum $
			[
				alignment x',
				alignment x',
				alignment pi_,
				alignment x',
				--alignment tm,
				alignment f,
				alignment i,

				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Swch: alignment accessed its argument!" :: Float

	peek ptr =
		((\swch -> swch & (swchP1 .~ (if' (swch^.swchP1 < 0) (swch^.swchP0) (swch^.swchP1)))) <$>) .
		((\swch -> swch & (swchTm .~ (round $ 1000.0*(swch^.swchT)))) <$>) . flip evalStateT 0 $ Swch <$>
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p
		peekf32dLE ptr' <*>  -- r
		peeki32LE ptr' <*>  -- pi

		(peekf32dLE ptr' <* peekf32dLE ptr') <*>  -- t (skip f32)
		pure 0 <*>  -- tm
		(peeki32LE ptr' <* peeki32LE ptr') <*>  -- f (skip i32)
		peeki32LE ptr' <*>  -- i

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'      -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Swch
			(Vec3 px py pz) r pi_ t _tm f i p0 p1
		) = flip evalStateT 0 $ do
			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz
			pokef32dLE ptr' r
			pokei32LE ptr' pi_

			pokef32dLE ptr' t >> pokef32dLE ptr' t
			--pokei32LE ptr' tm
			pokei32LE ptr' f >> pokei32LE ptr' f
			pokei32LE ptr' i

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr

instance Storable Bill where
	sizeOf
		~(Bill
			fl mi _t _d _w _h _rx _ry _rz _p p0 p1
		) = sum $
			[
				sizeOf fl,
				sizeOf mi,
				sizeOf x',
				sizeOf x',

				3 * sizeOf x',
				3 * sizeOf x',

				3 * sizeOf x',
				3 * sizeOf x',
				3 * sizeOf x',

				3 * sizeOf x',

				sizeOf p0,
				sizeOf p1
			]
		where x' = error "Internal error: sizeOf Bill: sizeOf accessed its argument!" :: Float
	alignment
		~(Bill
			fl mi _t _d _w _h _rx _ry _rz _p p0 p1
		) = max 1 . maximum $
			[
				alignment fl,
				alignment mi,
				alignment x',
				alignment x',

				alignment x',
				alignment x',

				alignment x',
				alignment x',
				alignment x',

				alignment x',

				alignment p0,
				alignment p1
			]
		where x' = error "Internal error: alignment Bill: alignment accessed its argument!" :: Float

	peek ptr = ((\bill -> bill & (billP1 .~ (if' (bill^.billP1 < 0) (bill^.billP0) (bill^.billP1)))) <$>) . flip evalStateT 0 $ Bill <$>
		peeki32LE ptr' <*>  -- fl
		peeki32LE ptr' <*>  -- mi
		peekf32dLE ptr' <*>  -- t
		peekf32dLE ptr' <*>  -- d

		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- w
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- h

		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- rx
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- ry
		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- rz

		(Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*>  -- p

		peeki32LE ptr' <*>  -- p0
		peeki32LE ptr'      -- p1

		where ptr' = castPtr ptr

	poke ptr
		(Bill
			fl mi t d (Vec3 wx wy wz) (Vec3 hx hy hz) (Vec3 rxx rxy rxz) (Vec3 ryx ryy ryz) (Vec3 rzx rzy rzz) (Vec3 px py pz) p0 p1
		) = flip evalStateT 0 $ do
			pokei32LE ptr' fl
			pokei32LE ptr' mi
			pokef32dLE ptr' t
			pokef32dLE ptr' d

			pokef32dLE ptr' wx >> pokef32dLE ptr' wy >> pokef32dLE ptr' wz
			pokef32dLE ptr' hx >> pokef32dLE ptr' hy >> pokef32dLE ptr' hz

			pokef32dLE ptr' rxx >> pokef32dLE ptr' rxy >> pokef32dLE ptr' rxz
			pokef32dLE ptr' ryx >> pokef32dLE ptr' ryy >> pokef32dLE ptr' ryz
			pokef32dLE ptr' rzx >> pokef32dLE ptr' rzy >> pokef32dLE ptr' rzz

			pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz

			pokei32LE ptr' p0
			pokei32LE ptr' p1

		where ptr' = castPtr ptr

instance Storable Ball where
	sizeOf    ~(Ball (Vec3 _px _py _pz) _r) = sum [3 * sizeOf x', sizeOf x']
		where x' = error "Internal error: sizeOf Ball: sizeOf accessed its argument!" :: Float
	alignment ~(Ball (Vec3 _px _py _pz) _r) = max 1 $ maximum [alignment x', alignment x']
		where x' = error "Internal error: alignment Ball: alignment accessed its argument!" :: Float
	peek ptr = flip evalStateT 0 $ Ball <$> (Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*> peekf32dLE ptr'
		where ptr' = castPtr ptr
	poke ptr (Ball (Vec3 px py pz) r) = flip evalStateT 0 $ pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz >> pokef32dLE ptr' r
		where ptr' = castPtr ptr

instance Storable View where
	sizeOf    ~(View (Vec3 _px _py _pz) (Vec3 _qx _qy _qz)) = sum [3 * sizeOf x', 3 * sizeOf x']
		where x' = error "Internal error: sizeOf View: sizeOf accessed its argument!" :: Float
	alignment ~(View (Vec3 _px _py _pz) (Vec3 _qx _qy _qz)) = max 1 $ maximum [alignment x', alignment x']
		where x' = error "Internal error: alignment View: alignment accessed its argument!" :: Float
	peek ptr = flip evalStateT 0 $ View <$> (Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr') <*> (Vec3 <$> peekf32dLE ptr' <*> peekf32dLE ptr' <*> peekf32dLE ptr')
		where ptr' = castPtr ptr
	poke ptr (View (Vec3 px py pz) (Vec3 qx qy qz)) = flip evalStateT 0 $ pokef32dLE ptr' px >> pokef32dLE ptr' py >> pokef32dLE ptr' pz >> pokef32dLE ptr' qx >> pokef32dLE ptr' qy >> pokef32dLE ptr' qz
		where ptr' = castPtr ptr

instance Storable Dict where
	sizeOf    ~(Dict ai aj) = sum [sizeOf ai, sizeOf aj]
	alignment ~(Dict ai aj) = max 1 $ maximum [alignment ai, alignment aj]
	peek ptr = flip evalStateT 0 $ Dict <$> peeki32LE ptr' <*> peeki32LE ptr'
		where ptr' = castPtr ptr
	poke ptr (Dict ai aj) = flip evalStateT 0 $ pokei32LE ptr' ai >> pokei32LE ptr' aj
		where ptr' = castPtr ptr