diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+                    Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar
+  22 rue de Plaisance, 75014 Paris, France
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. You just DO WHAT THE FUCK YOU WANT TO.
+
diff --git a/Maps.hs b/Maps.hs
new file mode 100644
--- /dev/null
+++ b/Maps.hs
@@ -0,0 +1,30 @@
+module Maps where
+
+import Data.Int
+import Data.Maybe
+import Numeric
+import Text.Parsec
+
+data Part = Part
+	{ start :: Int64
+	, end :: Int64
+	, name :: String
+	} deriving Show
+
+rh = fst . head . readHex
+
+dmesgLine = do
+	string "0x"
+	st <- many1 hexDigit
+	string "-0x"
+	en <- many1 hexDigit
+	string " : \""
+	name <- many $ noneOf "\""
+	string "\""
+	newline
+	return $ Just $ Part (rh st) (rh en) name
+
+emptyLine = newline >> pure Nothing
+
+dmesg :: String -> [Part]
+dmesg s = catMaybes $ either (error . show) id $ parse (many1 (dmesgLine <|> emptyLine)) "" s
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/binsm.cabal b/binsm.cabal
new file mode 100644
--- /dev/null
+++ b/binsm.cabal
@@ -0,0 +1,30 @@
+name:                binsm
+-- semver
+version:             0.1.1
+synopsis:            binary files splitter and merger
+description:         binary files splitter and merger, makes working with offsets in your shell less painful
+homepage:            https://github.com/l29ah/binsm
+license:             OtherLicense
+license-file:        LICENSE
+author:              Sergey Alirzaev
+maintainer:          zl29ah@gmail.com
+category:            Tools
+build-type:          Simple
+cabal-version:       >=1.10
+Tested-With:         GHC == 8.6.5
+
+Source-repository head
+  type:              git
+  location:          https://github.com/l29ah/binsm.git
+
+Source-repository this
+  type:              git
+  location:          https://github.com/l29ah/binsm.git
+  tag:               0.1.1
+
+executable binsm
+  main-is:             binsm.hs
+  build-depends:       base >=4.9 && <4.13, parsec >=3.1 && <3.2, bytestring >= 0.10 && < 0.11, optparse-applicative >= 0.13.0.0 && < 0.15, ansi-wl-pprint >= 0.6.7.3 && < 0.7
+  default-language:    Haskell2010
+  ghc-options:         -fno-warn-tabs
+  other-modules:       Maps
diff --git a/binsm.hs b/binsm.hs
new file mode 100644
--- /dev/null
+++ b/binsm.hs
@@ -0,0 +1,79 @@
+import Control.Monad
+import qualified Data.ByteString.Lazy as BL
+import Data.Maybe
+import Data.Semigroup ((<>))
+import Options.Applicative
+import Text.PrettyPrint.ANSI.Leijen (string)
+
+import Maps
+
+data Opts = Opts
+	{ mapFile :: String
+	, extractPartition :: Maybe String
+	, mergePartition :: Maybe String
+	, partitionFile :: Maybe String
+	--, flashromFormat :: Bool
+--	, imageFile :: Maybe String
+	}
+
+optsP :: Parser Opts
+optsP = Opts
+	<$> strOption
+		( long "map"
+		<> short 'm'
+		<> metavar "FILE"
+		<> helpDoc (Just $ string $ unlines [
+			"Map of the binary. The default format is the linux dmesg:",
+			"0x000000000000-0x000000040000 : \"u-boot\"",
+			"0x000000040000-0x000000050000 : \"u-boot-env\"",
+			"0x000000050000-0x000000180000 : \"kernel1\"",
+			"0x000000180000-0x000000f50000 : \"rootfs\"",
+			"0x000000000000-0x000001000000 : \"flash\""])
+		)
+	<*> optional (option str
+		( long "extract"
+		<> short 'e'
+		<> metavar "PARTITION"
+		<> help "Partition to extract"
+		))
+	<*> optional (option str
+		( long "merge"
+		<> short 'm'
+		<> metavar "PARTITION"
+		<> help "Partition to merge"
+		))
+	<*> optional (option str
+		( long "partfile"
+		<> short 'f'
+		<> metavar "FILE"
+		<> help "File to read the partition from/write partition to"
+		))
+{-
+	<*> optional (option str
+		( long "image"
+		<> short 'i'
+		<> metavar "FILE"
+		<> help "File to read the image from"
+		))
+-}
+
+getpart pnam dmap = fromMaybe (error $ "Can't find partition \"" ++ pnam ++ "\"") $ listToMaybe $ filter (\p -> name p == pnam) dmap
+
+binsm :: Opts -> IO ()
+binsm opts = do
+	mapp <- Prelude.readFile $ mapFile opts
+	let dmap = dmesg mapp
+	when (and [extractPartition opts /= Nothing, mergePartition opts /= Nothing]) $ error "cannot extract and merge at the same time"
+	img <- BL.getContents
+	when (extractPartition opts /= Nothing) $ do
+		let part = getpart (fromJust $ extractPartition opts) dmap
+		maybe BL.putStr BL.writeFile (partitionFile opts) $ BL.drop (start part) $ BL.take (end part) img
+	when (mergePartition opts /= Nothing) $ do
+		let part = getpart (fromJust $ mergePartition opts) dmap
+		partcont <- BL.readFile (fromMaybe (error "no partition file specified") $ partitionFile opts)
+		let filelen = BL.length partcont
+		let partlen = end part - start part
+		when (filelen /= partlen) $ error "partition input file doesn't fit the partition"
+		BL.putStr $ BL.concat [BL.take (start part) img, partcont, BL.drop (end part) img]
+
+main = execParser (info (helper <*> optsP) mempty) >>= binsm
