Skip to content

Commit

Permalink
added magic byte check support, updated readme, version bump, refacto…
Browse files Browse the repository at this point in the history
…red basic hex function + updated comments, updated cmd args parser for magic byte support, refactored conversion from String to a typed Format, updated tests
  • Loading branch information
cirquit committed May 4, 2016
1 parent b05cab1 commit beedb53
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 138 deletions.
10 changes: 6 additions & 4 deletions README.md
Expand Up @@ -30,6 +30,7 @@ I will try to make the tool as identical as possible to the corresponding tool a
* If you specify a non-existing directory as target, it will be created
* The length of the array is NOT preceded by `0x`
* You can use the flags in any order you want
* This tool will check the files via magic bytes

## Binaries

Expand Down Expand Up @@ -57,19 +58,20 @@ I will try to make the tool as identical as possible to the corresponding tool a
* the executable is now at `~/.cabal/bin/UTFTConverter`

#### If you want to build it by hand:
* download the JuicyPixels library - `cabal install JuicyPixels`
* download the JuicyPixels library - `cabal install JuicyPixels` | `stack install
* download the git repository - `git clone http://github.com/cirquit/UTFTConverter`
* run `make` and the binary is in the same directory OR
* run `cabal install` and the binary is in `/dist/build/` and in your home directory under `~/.cabal/bin/UTFTConverter`
* run `cabal install` and the binary is in `/dist/build/` and in your home directory under `~/.cabal/bin/UTFTConverter` OR
* run `stack build && stack install` and the binary is somewhere in your `.stack-work/`


## Installation via stack:

* Install stack [*nix](https://github.com/commercialhaskell/stack/blob/master/doc/GUIDE.md) / [Windows](https://github.com/cirquit/ffp-lib)
* Install stack [Official guide](https://github.com/commercialhaskell/stack/blob/master/doc/GUIDE.md) / [My simplified guide](https://github.com/cirquit/ffp-lib)
* open your shell and type `stack setup`
* after that `stack install UTFTConverter`
* look up the messages where your executable lies


#### To do:

* maybe add resizing with a basic linear algorithm
Expand Down
12 changes: 6 additions & 6 deletions UTFTConverter.cabal
@@ -1,12 +1,12 @@
name: UTFTConverter
category: Graphics, Text
version: 0.1.0.0
version: 0.1.0.1
x-revision: 6
license: MIT
cabal-version: >=1.10
license-file: LICENSE
author: Alexander Isenko
maintainer: Alexander Isenko <alex.isenko@googlemail.com>
maintainer: Alexander Isenko <alex.isenko@protonmail.com>
homepage: http://github.com/cirquit/UTFTConverter
bug-reports: http://github.com/cirquit/UTFTConverter/issues
build-type: Simple
Expand Down Expand Up @@ -42,6 +42,8 @@ description:
.
/Supported formats:/
.
The binary tests if the pictures are encoded correctly via magic bytes using <http://hackage.haskell.org/package/image-type ImageType>. If the extention is wrong, but the encoding is correct, it will still try to convert the picture.
.
The exported library is using <http://hackage.haskell.org/package/JuicyPixels JuicyPixels> to convert incoming
.
* '.gif'
Expand Down Expand Up @@ -78,11 +80,8 @@ description:
> >>> toHex 100
> 64
.
> >>> to4Hex 100
> >>> toNHex 6 100
> "0064"
.
> >>> to6Hex 100
> "000064"

extra-source-files:
Makefile
Expand All @@ -103,6 +102,7 @@ library
filepath >=1.3 && <1.5 ,
directory >=1.2 && <1.3 ,
time >=1.4 && <1.6 ,
image-type >=0.1.0.0 ,
bytestring >=0.10 && <0.11 ,
JuicyPixels >=3.2 && <3.3

Expand Down
70 changes: 34 additions & 36 deletions main-src/Main.hs
Expand Up @@ -6,19 +6,19 @@ import System.Directory (getCurrentDirectory, createDirectoryIfMissing,
import System.Environment (getArgs, getProgName)
import System.Exit (exitFailure)

import Format.Converter (pictureToC, pictureToRaw)
import Format.Converter (pictureToC, pictureToRaw, toMaybeFormat, Format(..))
import Format.C (Platform(..))

type Args = [String]

data FileType = C | Raw

data Option = Option
{ outdir :: FilePath
, platform :: Platform
, filetype :: Maybe FileType
, files :: [FilePath]
}
{ outdir :: FilePath
, platform :: Platform
, filetype :: Maybe FileType
, files :: [(Format, FilePath)]
}


main :: IO ()
Expand Down Expand Up @@ -78,41 +78,39 @@ argsParser ("/o" :xs) opt = do
-- files parser
argsParser (fp :xs) opt = do
exists <- doesFileExist fp
case (exists, takeExtension fp) of
(True, ".jpg") -> argsParser xs (opt { files = fp : files opt})
(True, ".jpeg") -> argsParser xs (opt { files = fp : files opt})
(True, ".jpe") -> argsParser xs (opt { files = fp : files opt})
(True, ".bmp") -> argsParser xs (opt { files = fp : files opt})
(True, ".png") -> argsParser xs (opt { files = fp : files opt})
(True, ".gif") -> argsParser xs (opt { files = fp : files opt})
(True, ".tga") -> argsParser xs (opt { files = fp : files opt})
(True, _) -> putStrLn ("WARNING: This format is not supported ~ " ++ fp) >> argsParser xs opt
(False, _) -> putStrLn ("WARNING: Unreconized flag ~ " ++ fp) >> argsParser xs opt
mext <- toMaybeFormat fp
case (exists, mext) of
(True, Just ext) -> argsParser xs (opt { files = (ext, fp) : files opt })
(True, Nothing) -> putStrLn ("WARNING: This format is not supported ~ " ++ fp) >> argsParser xs opt
(False, _) -> putStrLn ("WARNING: Unreconized flag ~ " ++ fp) >> argsParser xs opt

printOpt :: (FilePath, Platform, FileType) -> IO ()
printOpt (outdir', platform', C) = do
putStrLn $ "Converting to : .c array file(s)"
putStrLn $ "Target platform : " ++ show platform'
putStrLn $ "Output directory : " ++ outdir' ++ "\n"
putStrLn "Processing file(s):"
putStrLn $ unlines [ "Converting to : .c array file(s)"
, "Target platform : " ++ show platform'
, "Output directory : " ++ outdir' ++ "\n"
, "Processing file(s):"
]
printOpt (outdir', _, Raw) = do
putStrLn $ "Converting to : .raw file(s)"
putStrLn $ "Output directory : " ++ outdir' ++ "\n"
putStrLn "Processing file(s):"
putStrLn $ unlines [ "Converting to : .raw file(s)"
, "Output directory : " ++ outdir' ++ "\n"
, "Processing file(s):"
]

help :: IO ()
help = do
name <- getProgName
putStrLn "\nUsage: "
putStrLn $ " " ++ name ++ " <filespec> /c|r [/o <path>] [/t AVR|ARM|PIC32]\n"
putStrLn "<filespec>: File(s) to convert"
putStrLn "parameters: /c - Create output as .c array files"
putStrLn " /r - Create output as .raw files"
putStrLn " /o <path> - Set the output directory to <path>\n"
putStrLn " /t <platform> - Select target plaform"
putStrLn " AVR : Most Arduinos, Bobuion"
putStrLn " ARM : Arduino Due, Teensy, TI CC3200 LaunchPad"
putStrLn " PIC32 : All chipKit boards\n"
putStrLn "You must specify either /c or /r. All other parameters are optional."
putStrLn "If /o is ommited the current directory will be used for output."
putStrLn "If /t is ommited the target platform will be set to AVR."
putStrLn $ unlines [ "\nUsage: "
, " " ++ name ++ " <filespec> /c|r [/o <path>] [/t AVR|ARM|PIC32]\n"
, "<filespec>: File(s) to convert"
, "parameters: /c - Create output as .c array files"
, " /r - Create output as .raw files"
, " /o <path> - Set the output directory to <path>\n"
, " /t <platform> - Select target plaform"
, " AVR : Most Arduinos, Bobuion"
, " ARM : Arduino Due, Teensy, TI CC3200 LaunchPad"
, " PIC32 : All chipKit boards\n"
, "You must specify either /c or /r. All other parameters are optional."
, "If /o is ommited the current directory will be used for output."
, "If /t is ommited the target platform will be set to AVR."
]
10 changes: 5 additions & 5 deletions src/Format/C.hs
Expand Up @@ -11,7 +11,7 @@
module Format.C (toCFile, Platform(..)) where

import Data.Time (UTCTime)
import Format.RGB565 (to4Hex)
import Format.RGB565 (toNHex)


-- | The Platform datatype has three constructors, for every supported architecture
Expand All @@ -26,9 +26,9 @@ data Platform = AVR
--
-- * @[String]@ are the hex strings
-- * @(String, String)@ is the (filename, file extention)
-- * @(Int, Int) is the (width, height)
-- * UTCTime is the current time
-- * Platform is the desired platform to convert to
-- * @(Int, Int)@ is the (width, height)
-- * @UTCTime@ is the current time
-- * @Platform@ is the desired platform to convert to
--
-- The result is a the string which is the full c file with header + unsigned short array + comments
--
Expand Down Expand Up @@ -81,4 +81,4 @@ printHex :: String -> String
printHex hx = '0':'x': hx ++ ", "

printCom :: Int -> String
printCom n = ' ':' ':'/':'/':' ':'0':'x': to4Hex n ++ ' ':'(': show n ++ ") pixels\n"
printCom n = ' ':' ':'/':'/':' ':'0':'x': toNHex 4 n ++ ' ':'(': show n ++ ") pixels\n"
115 changes: 87 additions & 28 deletions src/Format/Converter.hs
Expand Up @@ -20,13 +20,14 @@

-----------------------------------------------------------------------------

module Format.Converter (pictureToRaw, pictureToC) where
module Format.Converter (pictureToRaw, pictureToC, toMaybeFormat, Format(..)) where

import qualified Data.ByteString as BS (readFile)
import Data.ByteString.Lazy (toStrict)
import System.FilePath.Posix (takeBaseName, (</>),
takeExtension, takeFileName)
import Data.Time (getCurrentTime)
import Data.Char (toUpper)

import Codec.Picture.Types
import Codec.Picture.Saving (imageToBitmap)
Expand All @@ -36,17 +37,94 @@ import Codec.Picture.Png (decodePng)
import Codec.Picture.Gif (decodeGif)
import Codec.Picture.Tga (decodeTga)

import Codec.ImageType (getFileType)

import Format.RGB565 (toRGB565Hex)
import Format.C (toCFile, Platform())
import Format.Raw (toRawFile)

-- | Currently supported picture formats (by JuicyPixels)
--
data Format = Jpeg
| Bmp
| Png
| Gif
| Tga

instance Show Format where
show Jpeg = "jpeg"
show Bmp = "bmp"
show Png = "png"
show Gif = "gif"
show Tga = "tga"

instance Read Format where
readsPrec _ e = do
(s,r) <- lex e
case map toUpper s of
"JPEG" -> return (Jpeg, r)
"JPG" -> return (Jpeg, r)
"JPE" -> return (Jpeg, r)
"GIF" -> return (Gif, r)
"BMP" -> return (Bmp, r)
"PNG" -> return (Png, r)
"TGA" -> return (Tga, r)
_ -> fail "Read Format: no parse"

-- | Checking if the format is supported via magic bytes
-- safe checking for everything unless __.tga__
--
-- __Possible errors:__
--
-- * It will be problematic in the future if you try to read the file as .tga if it's not encoded as one
--
-- __Example usage:__
--
-- @
-- λ> toMaybeFormat "cat_01_bmp_120x120.bmp"
-- Just Bmp
-- λ> toMaybeFormat "cat_01_bmp_120x120.jpeg"
-- Just Jpeg
-- λ> toMaybeFormat "cat_01_bmp_120x120.jpe"
-- Just Jpeg
-- @

toMaybeFormat :: FilePath -> IO (Maybe Format)
toMaybeFormat fp = do
mtp <- getFileType fp
let ext = drop 1 $ takeExtension fp
validFormats = map show [Jpeg, Bmp, Png, Gif, Tga]
case (mtp, ext) of
(Just "jpeg", _)
| ext `elem` ["jpg", "jpeg", "jpe"] -> return $ Just Jpeg
(Just tp, _)
| tp `elem` validFormats, tp == ext -> return $ Just $ read tp
| tp `elem` validFormats -> do
putStrLn $ "WARNING: File format is " ++ '.':tp ++ ", not " ++ '.':ext ++ " ~ " ++ fp
return $ Just $ read fp
(_, _)
| "tga" <- ext -> return $ Just Tga
| otherwise -> return Nothing


formatToDynImg :: Format -> FilePath -> IO (Maybe DynamicImage)
formatToDynImg f fp = do
case f of
Jpeg -> jpgToDynImg fp
Bmp -> bmpToDynImg fp
Png -> pngToDynImg fp
Gif -> gifToDynImg fp
Tga -> tgaToDynImg fp
-- ft@_ -> error $ "Converter.hs: formatToDynImg:91 - Unsupported Format - " ++ show ft


-- | pictureToRaw takes a picture, decodes it, parses every pixel to a 4 digit RGB565 hex and saves it to
-- a file with the same name and a @.raw@ extention in the specified directory
--
-- This function takes two arguments
--
-- * first @FilePath@ is the directory to save the file to
-- * second @FilePath@ is the filepath to the picture
-- * second @(Format, FilePath)@ is the format and filepath of the picture
--
-- __Possible errors:__
--
Expand All @@ -65,22 +143,12 @@ import Format.Raw (toRawFile)
--
-- @
-- λ> dir <- getCurrentDirectory
-- λ> pictureToRaw dir "cat_01_bmp_120x120.bmp"
-- λ> pictureToRaw dir (Bmp, "cat_01_bmp_120x120.bmp")
-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.raw
-- @

pictureToRaw :: FilePath -> FilePath -> IO ()
pictureToRaw saveTo fp = do
case takeExtension fp of
(".jpg") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp
(".jpeg") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp
(".jpe") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp
(".bmp") -> bmpToDynImg fp >>= dynimgToRaw saveTo fp
(".png") -> pngToDynImg fp >>= dynimgToRaw saveTo fp
(".gif") -> gifToDynImg fp >>= dynimgToRaw saveTo fp
(".tga") -> tgaToDynImg fp >>= dynimgToRaw saveTo fp
(_) -> error "Argument filter let through some unsupported types"

pictureToRaw :: FilePath -> (Format, FilePath) -> IO ()
pictureToRaw saveTo (format,fp) = formatToDynImg format fp >>= dynimgToRaw saveTo fp

-- | pictureToC takes a picture, decodes it, parses every pixel to a 4 digit RGB565 hex, adds the header
-- based on the desired platform and saves it to a file with the same name and a @.c@ extention in the specified
Expand All @@ -90,7 +158,7 @@ pictureToRaw saveTo fp = do
--
-- * @Platform@ is the desired platform to convert to
-- * first @FilePath@ is the directory to save the file to
-- * second @FilePath@ is the filepath to the picture
-- * second @(Format, FilePath)@ is the format and filepath of the picture
--
-- __Possible errors:__
--
Expand All @@ -109,21 +177,12 @@ pictureToRaw saveTo fp = do
--
-- @
-- λ> dir <- getCurrentDirectory
-- λ> pictureToC AVR dir "cat_01_bmp_120x120.bmp"
-- λ> pictureToC AVR dir (Bmp, "cat_01_bmp_120x120.bmp")
-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.c
-- @

pictureToC :: Platform -> FilePath -> FilePath -> IO ()
pictureToC platform saveTo fp = do
case takeExtension fp of
(".jpg") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp
(".jpeg") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp
(".jpe") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp
(".bmp") -> bmpToDynImg fp >>= dynimgToC platform saveTo fp
(".png") -> pngToDynImg fp >>= dynimgToC platform saveTo fp
(".gif") -> gifToDynImg fp >>= dynimgToC platform saveTo fp
(".tga") -> tgaToDynImg fp >>= dynimgToC platform saveTo fp
(_) -> error "Argument filter let through some unsupported types"
pictureToC :: Platform -> FilePath -> (Format, FilePath) -> IO ()
pictureToC platform saveTo (format,fp) = formatToDynImg format fp >>= dynimgToC platform saveTo fp

jpgToDynImg :: FilePath -> IO (Maybe DynamicImage)
jpgToDynImg fp = do
Expand Down

0 comments on commit beedb53

Please sign in to comment.