概要
binaryパッケージのGetとrunGetを使っていきます。
Parsecを使ったことがある人なら感覚的にわかると思います。
今回は例としてMMDのモデルデータの形式であるpmxのバージョンを調べたいと思います
pmxの仕様
pmxの仕様はなんとpmxエディタに同梱されてます。
とある工房でpmxエディタをダウンロードしたらLib/PMX仕様/PMX仕様.txtにあります。
154行目あたりを見ると分かる通り、pmxのヘッダは"PMX "というASCII文字列(4バイト)の後にヴァージョンがfloat(4バイト)で書いてあるみたいなのでこれを読みましょう!
Haskellの方の準備
stack new pmx simple
してpmx.cabalはこんなかんじで
pmx.cabal
name: pmx
version: 0.1.0.0
synopsis: Simple project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/pmx#readme
license: BSD3
license-file: LICENSE
author: Author name here
maintainer: example@example.com
copyright: 2016 Author name here
category: Web
build-type: Simple
cabal-version: >=1.10
executable pmx
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5
, bytestring
, binary >= 0.8.4.0
読み込み
コード
大体見たらわかると思います。
入力された名前のpmxファイルを開いてパースしてPmxHeader型にして表示するだけ。
GetはMonadとApplicativeのインスタンスになっているのでParsecでパーサ書くみたいな感覚で書けます。
注意点としてpmxはバイト順がリトルエンディアンなのでバージョンを取得するのにgetFloatle
を使ってやる必要があります。
Main.hs
module Main where
import Control.Applicative ()
import System.IO
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString as B
import Data.Binary.Get
import Data.Char (chr)
data PmxHeader = PmxHeader { getPmxVersion :: Float
} deriving (Show, Read)
main :: IO ()
main = do
putStr ">> "
hFlush stdout
s <- getLine
readPmx s
readPmx :: String -> IO ()
readPmx f = do
rh <- openFile f ReadMode
i <- BL.hGetContents rh
let d = runGet parsePmxHeader i
print d
parsePmxHeader :: Get PmxHeader
parsePmxHeader = PmxHeader <$> parsePmxVersion
parsePmxVersion :: Get Float
parsePmxVersion = do
s <- getString 4
if s == "PMX " then getFloat else return 1.0
getString :: Int -> Get String
getString n = map (chr . fromEnum) . B.unpack <$> getByteString n
getInt :: Get Int
getInt = fromIntegral <$> getWord32le
getFloat :: Get Float
getFloat = getFloatle
実行結果
$ stack build && stack exec pmx
Setting codepage to UTF-8 (65001) to ensure correct output from GHC
>> a.pmx
PmxHeader {getPmxVersion = 2.0}
今回はここまで。