octane-0.10.0: library/Octane/Type/Replay.hs
{-# LANGUAGE DeriveGeneric #-}
module Octane.Type.Replay (Replay(..)) where
import Data.Function ((&))
import qualified Control.DeepSeq as DeepSeq
import qualified Control.Monad as Monad
import qualified Data.Aeson as Aeson
import qualified Data.Binary as Binary
import qualified Data.Binary.Get as Binary
import qualified Data.ByteString.Lazy as LazyBytes
import qualified GHC.Generics as Generics
import qualified Octane.Json as Json
import qualified Octane.Type.Actor as Actor
import qualified Octane.Type.CacheItem as CacheItem
import qualified Octane.Type.KeyFrame as KeyFrame
import qualified Octane.Type.Mark as Mark
import qualified Octane.Type.Message as Message
import qualified Octane.Type.Primitive.Dictionary as Dictionary
import qualified Octane.Type.Primitive.Int32 as Int32
import qualified Octane.Type.Primitive.List as List
import qualified Octane.Type.Primitive.Stream as Stream
import qualified Octane.Type.Primitive.Text as Text
import qualified Octane.Type.Primitive.Word32 as Word32
import qualified Octane.Type.Property as Property
import qualified Octane.Utility as Utility
import qualified Text.Printf as Printf
-- | An entire replay. All of the metadata has been parsed, but the actual net
-- stream has not.
data Replay = Replay
-- Number of bytes in the first section.
{ replaySize1 :: !Word32.Word32
-- CRC to check the first section.
, replayCRC1 :: !Word32.Word32
-- Major replay version number.
, replayVersion1 :: !Int32.Int32
-- Minor replay version number.
, replayVersion2 :: !Int32.Int32
-- Label, which is always "TAGame.Replay_Soccar_TA".
, replayLabel :: !Text.Text
-- High-level metadata about the replay.
, replayProperties :: !(Dictionary.Dictionary Property.Property)
-- Number of bytes in the last section.
, replaySize2 :: !Word32.Word32
-- CRC to check the last section.
, replayCRC2 :: !Word32.Word32
-- Array of strings for all of the levels that need to be loaded (array
-- length followed by each string)
, replayLevels :: !(List.List Text.Text)
-- Array of Keyframe information used for timeline scrubbing (array
-- length followed by each keyframe struct) (Time, Frame, File Position)
, replayKeyFrames :: !(List.List KeyFrame.KeyFrame)
-- Array of bytes that is the bulk of the data. This is the raw network
-- stream. (array length followed by a bunch of bytes)
, replayStream :: !Stream.Stream
-- Array of debugging logs (strings). This reminds me that I should
-- probably turn these off to make the replays smaller. (array length
-- followed by each string)
, replayMessages :: !(List.List Message.Message)
-- Array of information used to display the Tick marks in the replay (goal
-- scores). (array length followed by each tick struct) (Type, Frame)
, replayMarks :: !(List.List Mark.Mark)
-- Array of strings of replicated Packages
, replayPackages :: !(List.List Text.Text)
-- Array of strings for the Object table. Whenever a persistent object gets
-- referenced in the network stream its path gets added to this array. Then
-- its index in this array is used in the network stream.
, replayObjects :: !(List.List Text.Text)
-- Array of strings for the Name table. "Names" are commonly used strings
-- that get assigned an integer for use in the network stream.
, replayNames :: !(List.List Text.Text)
-- Map of string, integer pairs for the Class Index Map. Whenever a class
-- is used in the network stream it is given an integer id by this map.
, replayActors :: !(List.List Actor.Actor)
-- "Class Net Cache Map" maps each replicated property in a class to an
-- integer id used in the network stream.
, replayCacheItems :: !(List.List CacheItem.CacheItem)
} deriving (Eq,Generics.Generic,Show)
instance Binary.Binary Replay where
get = do
size1 <- Binary.get
crc1 <- Binary.get
data1 <- Binary.getLazyByteString (Word32.fromWord32 size1)
let actualCRC1 = Utility.crc32 data1
let expectedCRC1 = Word32.fromWord32 crc1
Monad.when (actualCRC1 /= expectedCRC1) (fail (Printf.printf
"First CRC 0x%08x does not match expected value 0x%08x"
actualCRC1 expectedCRC1))
size2 <- Binary.get
crc2 <- Binary.get
data2 <- Binary.getLazyByteString (Word32.fromWord32 size2)
let actualCRC2 = Utility.crc32 data2
let expectedCRC2 = Word32.fromWord32 crc2
Monad.when (actualCRC2 /= expectedCRC2) (fail (Printf.printf
"Second CRC 0x%08x does not match expected value 0x%08x"
actualCRC2 expectedCRC2))
pure (flip Binary.runGet (LazyBytes.append data1 data2) (do
version1 <- Binary.get
version2 <- Binary.get
label <- Binary.get
properties <- Binary.get
levels <- Binary.get
keyFrames <- Binary.get
stream <- Binary.get
messages <- Binary.get
marks <- Binary.get
packages <- Binary.get
objects <- Binary.get
names <- Binary.get
actors <- Binary.get
cacheItems <- Binary.get
pure Replay
{ replaySize1 = size1
, replayCRC1 = crc1
, replayVersion1 = version1
, replayVersion2 = version2
, replayLabel = label
, replayProperties = properties
, replaySize2 = size2
, replayCRC2 = crc2
, replayLevels = levels
, replayKeyFrames = keyFrames
, replayStream = stream
, replayMessages = messages
, replayMarks = marks
, replayPackages = packages
, replayObjects = objects
, replayNames = names
, replayActors = actors
, replayCacheItems = cacheItems
}))
put replay = do
replay & replaySize1 & Binary.put
replay & replayCRC1 & Binary.put
replay & replayVersion1 & Binary.put
replay & replayVersion2 & Binary.put
replay & replayLabel & Binary.put
replay & replayProperties & Binary.put
replay & replaySize2 & Binary.put
replay & replayCRC2 & Binary.put
replay & replayLevels & Binary.put
replay & replayKeyFrames & Binary.put
replay & replayStream & Binary.put
replay & replayMessages & Binary.put
replay & replayMarks & Binary.put
replay & replayPackages & Binary.put
replay & replayObjects & Binary.put
replay & replayNames & Binary.put
replay & replayActors & Binary.put
replay & replayCacheItems & Binary.put
instance DeepSeq.NFData Replay
instance Aeson.ToJSON Replay where
toJSON = Aeson.genericToJSON (Json.toJsonOptions "Replay")