module Rattletrap.Primitive.Text where import Rattletrap.Primitive.Int32 import Rattletrap.Utility import qualified Data.Binary as Binary import qualified Data.Binary.Bits.Get as BinaryBit import qualified Data.Binary.Bits.Put as BinaryBit import qualified Data.Binary.Get as Binary import qualified Data.Binary.Put as Binary import qualified Data.ByteString.Lazy as ByteString import qualified Data.Char as Char import qualified Data.Text as Text import qualified Data.Text.Encoding as Encoding newtype Text = Text { textValue :: Text.Text } deriving (Eq, Ord, Show) getText :: Binary.Get Text getText = do rawSize <- getInt32 let decode = getTextDecoder rawSize let size = normalizeTextSize rawSize bytes <- Binary.getLazyByteString size let text = dropNull (decode bytes) pure (Text text) putText :: Text -> Binary.Put putText text = do let size = getTextSize text let encode = getTextEncoder size putInt32 size Binary.putLazyByteString (encode (addNull (textValue text))) getTextBits :: BinaryBit.BitGet Text getTextBits = do rawSize <- getInt32Bits let decode = getTextDecoder rawSize let size = normalizeTextSize rawSize bytes <- BinaryBit.getLazyByteString size let text = dropNull (decode (reverseBytes bytes)) pure (Text text) putTextBits :: Text -> BinaryBit.BitPut () putTextBits text = do let size = getTextSize text let encode = getTextEncoder size putInt32Bits size BinaryBit.putByteString (ByteString.toStrict (reverseBytes (encode (addNull (textValue text))))) stringToText :: String -> Text stringToText string = Text (Text.pack string) textToString :: Text -> String textToString text = Text.unpack (textValue text) getTextSize :: Text -> Int32 getTextSize text = let value = textValue text scale = if Text.all Char.isLatin1 value then 1 else -1 rawSize = if Text.null value then 0 else fromIntegral (Text.length value) + 1 size = if value == Text.pack "\x00\x00\x00None" then 0x05000000 else scale * rawSize in Int32 size normalizeTextSize :: Integral a => Int32 -> a normalizeTextSize size = case int32Value size of 0x05000000 -> 8 x -> if x < 0 then (-2 * fromIntegral x) else fromIntegral x getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text getTextDecoder size bytes = let decode = if size < Int32 0 then Encoding.decodeUtf16LE else Encoding.decodeLatin1 in decode (ByteString.toStrict bytes) getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString getTextEncoder size text = if size < Int32 0 then ByteString.fromStrict (Encoding.encodeUtf16LE text) else encodeLatin1 text dropNull :: Text.Text -> Text.Text dropNull = Text.dropWhileEnd (== '\x00') addNull :: Text.Text -> Text.Text addNull text = if Text.null text then text else Text.snoc text '\x00'