ecu-0.0.1: src/ToESB.hs
module Main (main) where
import Data.Bits
import qualified Data.ByteString as BS
import Data.Char
import Data.Digest.CRC32
import Data.List
import Data.Word
import System.Environment
import Text.Printf
help :: IO ()
help = putStrLn $ unlines
[ ""
, " NAME"
, " toesb - converts an s-record ecu image to the Eaton Standard Binary (ESB) format"
, ""
, " SYNOPSIS"
, " toesb [ --nocrc ] <input-file>"
, ""
, " OPTIONS"
, " --nocrc Ignore CRC checking on s-record."
, ""
]
main :: IO ()
main = do
args <- getArgs
case args of
[i] -> readFile i >>= srec2esb (out i) True
["--nocrc", i] -> readFile i >>= srec2esb (out i) False
_ -> help
out :: FilePath -> FilePath
out a = takeWhile (/= '.') a ++ ".esb"
srec2esb :: FilePath -> Bool -> String -> IO ()
srec2esb o checkCRC a = do
printf "section address section length\n"
sequence_ [ printf "0x%08X 0x%08X\n" a (length b) | (a, b) <- a3 ]
putStrLn $ "writing " ++ o ++ " ..."
BS.writeFile o $ BS.pack $ map fromIntegral file
where
a1 = [ (dehex $ init $ init $ drop 2 a, head $ dehex $ reverse $ take 2 $ reverse a) | l <- lines a, let a = filter isAlphaNum l, isPrefixOf "S3" a ]
a2 = concatMap (addr checkCRC) a1
a3 = [ (a, b) | (a, b) <- blocks a2, a >= 0x10000, a <= 0x7FFFF ]
n = length a3
allBlocks = le n ++ concatMap j1939Block a3
fileCRC = fromIntegral $ crc32 $ (map fromIntegral allBlocks :: [Word8])
file = [0xE5, 0x5B, 0xBE, 0xE5, 5, 0, 0, 0] ++ le fileCRC ++ allBlocks
addr :: Bool -> ([Int], Int) -> [(Int, Int)] -- (addr, byte)
addr checkCRC (a, checksum) = if checkCRC && complement (sum a) .&. 0xFF /= checksum then error $ "failed checksum: " ++ show (a, checksum) else zip addrs bytes
where
base = shiftL (a !! 1) 24
+ shiftL (a !! 2) 16
+ shiftL (a !! 3) 8
+ (a !! 4)
addrs = [base ..]
bytes = drop 5 a
blocks :: [(Int, a)] -> [(Int, [a])]
blocks [] = []
blocks ((addr, byte) : rest) = section : blocks rest'
where
(section, rest') = block (addr + 1) (addr, [byte]) rest
block :: Int -> (Int, [a]) -> [(Int, a)] -> ((Int, [a]), [(Int, a)])
block nextAddr (startingAddr, sofar) ((a, b):rest) | a == nextAddr = block (nextAddr + 1) (startingAddr, b : sofar) rest
block _ (a, b) rest = ((a, reverse b), rest)
dehex :: String -> [Int]
dehex "" = []
dehex (a:b:c) = read ("0x" ++ [a,b]) : dehex c
dehex _ = undefined
j1939Block :: (Int, [Int]) -> [Int]
j1939Block (addr, d) = [3, 0, 0, 0] ++ le (shiftL (addr - 0x10000) 8 .&. 0xFFFFFF00 .|. 128) ++ le (length d) ++ d
le :: Int -> [Int]
le a = [ a .&. 0xFF]
++ [shiftR a 8 .&. 0xFF]
++ [shiftR a 16 .&. 0xFF]
++ [shiftR a 24 .&. 0xFF]