diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt
index 5857132..37d461f 100644
--- a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt
+++ b/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt
@@ -10,7 +10,8 @@ import eu.depau.etchdroid.StateKeeper
 import eu.depau.etchdroid.enums.FlashMethod
 import eu.depau.etchdroid.enums.WizardStep
 import eu.depau.etchdroid.kotlin_exts.*
-import eu.depau.etchdroid.services.UsbAPIWriteService
+import eu.depau.etchdroid.services.UsbApiDmgWriteService
+import eu.depau.etchdroid.services.UsbApiImgWriteService
 import kotlinx.android.synthetic.main.fragment_confirminfo.view.*
 import java.io.IOException
 
@@ -29,7 +30,12 @@ class ConfirmInfoFragment : WizardFragment() {
 
         context?.toast("Check notification for progress")
 
-        val intent = Intent(activity, UsbAPIWriteService::class.java)
+        val intent: Intent = when (StateKeeper.flashMethod) {
+            FlashMethod.FLASH_API -> Intent(activity, UsbApiImgWriteService::class.java)
+            FlashMethod.FLASH_DMG_API -> Intent(activity, UsbApiDmgWriteService::class.java)
+            else -> null!!
+        }
+
         intent.setDataAndType(StateKeeper.imageFile, "application/octet-stream")
         intent.putExtra("usbDevice", StateKeeper.usbDevice)
         activity?.startService(intent)
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbAPIWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbAPIWriteService.kt
deleted file mode 100644
index 2256085..0000000
--- a/app/src/main/java/eu/depau/etchdroid/services/UsbAPIWriteService.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package eu.depau.etchdroid.services
-
-import android.content.Intent
-import android.hardware.usb.UsbDevice
-import android.net.Uri
-import android.util.Log
-import com.github.mjdev.libaums.UsbMassStorageDevice
-import eu.depau.etchdroid.kotlin_exts.getFileName
-import eu.depau.etchdroid.kotlin_exts.getFileSize
-import eu.depau.etchdroid.kotlin_exts.name
-import java.nio.ByteBuffer
-
-class UsbAPIWriteService : UsbWriteService("UsbAPIWriteService") {
-    // 512 * 32 bytes = USB max transfer size
-    val DD_BLOCKSIZE = 512 * 32 * 64  // 1 MB
-
-    class Action {
-        val WRITE_IMAGE = "eu.depau.etchdroid.action.API_WRITE_IMAGE"
-        val WRITE_CANCEL = "eu.depau.etchdroid.action.API_WRITE_CANCEL"
-    }
-
-    private fun getUsbMSDevice(usbDevice: UsbDevice): UsbMassStorageDevice? {
-        val msDevs = UsbMassStorageDevice.getMassStorageDevices(this)
-
-        for (dev in msDevs) {
-            if (dev.usbDevice == usbDevice)
-                return dev
-        }
-
-        return null
-    }
-
-    override fun writeImage(intent: Intent): Long {
-        val uri: Uri = intent.data!!
-        val usbDevice: UsbDevice = intent.getParcelableExtra("usbDevice")
-
-        val msDev = getUsbMSDevice(usbDevice)!!
-        msDev.init()
-
-        val blockDev = msDev.blockDevice
-        val bsFactor = DD_BLOCKSIZE / blockDev.blockSize
-        val byteBuffer = ByteBuffer.allocate(blockDev.blockSize * bsFactor)
-        val imageSize = uri.getFileSize(this)
-        val inputStream = contentResolver.openInputStream(uri)!!
-
-        val startTime = System.currentTimeMillis()
-
-        var readBytes: Int
-        var offset = 0L
-        var writtenBytes: Long = 0
-
-        try {
-            while (true) {
-                wakeLock(true)
-                readBytes = inputStream.read(byteBuffer.array()!!)
-                if (readBytes < 0)
-                    break
-                byteBuffer.position(0)
-
-                blockDev.write(offset, byteBuffer)
-                offset += bsFactor
-                writtenBytes += readBytes
-
-                updateNotification(usbDevice.name, uri.getFileName(this), offset * blockDev.blockSize, imageSize)
-            }
-
-            resultNotification(usbDevice.name, uri.getFileName(this)!!, true, writtenBytes, startTime)
-        } catch (e: Exception) {
-            resultNotification(usbDevice.name, uri.getFileName(this)!!, false, writtenBytes, startTime)
-            Log.e(TAG, "Could't write image to ${usbDevice.name}")
-            throw e
-        } finally {
-            wakeLock(false)
-            msDev.close()
-        }
-
-        Log.d(TAG, "Written $writtenBytes bytes to ${usbDevice.name} using API")
-        return writtenBytes
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt
new file mode 100644
index 0000000..6e257b7
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiDmgWriteService.kt
@@ -0,0 +1,115 @@
+package eu.depau.etchdroid.services
+
+import android.hardware.usb.UsbDevice
+import android.net.Uri
+import com.google.common.util.concurrent.SimpleTimeLimiter
+import com.google.common.util.concurrent.TimeLimiter
+import com.google.common.util.concurrent.UncheckedTimeoutException
+import eu.depau.etchdroid.kotlin_exts.getBinary
+import eu.depau.etchdroid.kotlin_exts.getFileName
+import eu.depau.etchdroid.kotlin_exts.getFileSize
+import eu.depau.etchdroid.kotlin_exts.name
+import java.io.BufferedReader
+import java.io.InputStream
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+val plistRegex = Regex("\\s*partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s*")
+//val progressRegex = Regex("\\[?(\\d+)]\\s+(\\d+[.,]\\d+)%")
+
+class UsbApiDmgWriteService : UsbApiWriteService("UsbApiDmgWriteService") {
+    val SECTOR_SIZE = 512
+
+    private lateinit var uri: Uri
+    private lateinit var process: Process
+    private lateinit var errReader: BufferedReader
+
+    private var bytesTotal = 0
+
+    private var readTimeLimiter: TimeLimiter = SimpleTimeLimiter.create(Executors.newCachedThreadPool())
+
+    override fun getSendProgress(usbDevice: UsbDevice, uri: Uri): (Long) -> Unit {
+        val imageSize = uri.getFileSize(this)
+        return { bytes ->
+            //            asyncReadProcessProgress()
+
+            try {
+                readTimeLimiter.callWithTimeout({
+                    val byteArray = ByteArray(128)
+                    process.errorStream.read(byteArray)
+                    System.err.write(byteArray)
+                }, 100, TimeUnit.MILLISECONDS)
+            } catch (e: TimeoutException) {
+            } catch (e: UncheckedTimeoutException) {
+            }
+
+            val perc = if (bytesTotal == 0)
+                -1
+            else
+                (bytes.toDouble() / bytesTotal.toDouble() * 100).toInt()
+
+            updateNotification(usbDevice.name, uri.getFileName(this), bytes, perc)
+        }
+    }
+//
+//    fun asyncReadProcessProgress() {
+//        val startTime = System.currentTimeMillis()
+//        var c: Int
+//        val charArray = CharArray(20)
+//
+//        try {
+//            while (System.currentTimeMillis() < startTime + 50) {
+//                // Skip everything until the first backspace
+//                do
+//                    c = readTimeLimiter.callWithTimeout(errReader::read, 50, TimeUnit.MILLISECONDS)
+//                while (c.toChar() != '\b' && c != -1)
+//                // Skip all backspaces
+//                do
+//                    c = readTimeLimiter.callWithTimeout(errReader::read, 50, TimeUnit.MILLISECONDS)
+//                while (c.toChar() == '\b' && c != -1)
+//
+//                // Read the stream
+//                readTimeLimiter.callWithTimeout({errReader.read(charArray)}, 50, TimeUnit.MILLISECONDS)
+//                val match = progressRegex.find(String(charArray)) ?: continue
+//                val (blocksruncurStr, percStr) = match.destructured
+//                val blocksruncur = blocksruncurStr.toInt()
+//
+//                blocksrun += blocksruncur
+//
+//                if (blocksruncur >= blocksrunLast)
+//                    blocksrun -= blocksrunLast
+//
+//                blocksrunLast = blocksruncur
+//            }
+//        } catch (e: TimeoutException) {
+//        } catch (e: UncheckedTimeoutException) {
+//        }
+//    }
+
+    override fun getInputStream(uri: Uri): InputStream {
+        this.uri = uri
+        val pb = ProcessBuilder(getBinary("dmg2img").path, "-v", uri.path, "-")
+        pb.environment()["LD_LIBRARY_PATH"] = applicationInfo.nativeLibraryDir
+        process = pb.start()
+        errReader = process.errorStream.bufferedReader()
+
+        // Read blocksruncount
+        var matched = false
+        var lastSector = 0
+        while (true) {
+            val line = errReader.readLine() ?: break
+            val match = plistRegex.find(line) ?: if (matched) break else continue
+            matched = true
+
+            val (begin, size, decoded, firstsector, sectorcount, blocksruncount) = match.destructured
+
+            val partLastSector = firstsector.toInt() + sectorcount.toInt()
+            if (partLastSector > lastSector)
+                lastSector = partLastSector
+        }
+
+        bytesTotal = lastSector * SECTOR_SIZE
+        return process.inputStream
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt
new file mode 100644
index 0000000..9e86d62
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiImgWriteService.kt
@@ -0,0 +1,21 @@
+package eu.depau.etchdroid.services
+
+import android.hardware.usb.UsbDevice
+import android.net.Uri
+import eu.depau.etchdroid.kotlin_exts.getFileName
+import eu.depau.etchdroid.kotlin_exts.getFileSize
+import eu.depau.etchdroid.kotlin_exts.name
+import java.io.InputStream
+
+class UsbApiImgWriteService : UsbApiWriteService("UsbApiImgWriteService") {
+    override fun getSendProgress(usbDevice: UsbDevice, uri: Uri): (Long) -> Unit {
+        val imageSize = uri.getFileSize(this)
+        return { bytes ->
+            updateNotification(usbDevice.name, uri.getFileName(this), bytes, (bytes.toFloat() / imageSize * 100).toInt())
+        }
+    }
+
+    override fun getInputStream(uri: Uri): InputStream {
+        return contentResolver.openInputStream(uri)!!
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt
new file mode 100644
index 0000000..3edb8cd
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/services/UsbApiWriteService.kt
@@ -0,0 +1,117 @@
+package eu.depau.etchdroid.services
+
+import android.content.Intent
+import android.hardware.usb.UsbDevice
+import android.net.Uri
+import android.util.Log
+import com.github.mjdev.libaums.UsbMassStorageDevice
+import eu.depau.etchdroid.kotlin_exts.getFileName
+import eu.depau.etchdroid.kotlin_exts.name
+import java.io.BufferedInputStream
+import java.io.InputStream
+import java.nio.ByteBuffer
+
+abstract class UsbApiWriteService(name: String) : UsbWriteService(name) {
+    // 512 * 32 bytes = USB max transfer size
+    val DD_BLOCKSIZE = 512 * 32 * 64  // 1 MB
+
+    class Action {
+        val WRITE_IMAGE = "eu.depau.etchdroid.action.API_WRITE_IMAGE"
+        val WRITE_CANCEL = "eu.depau.etchdroid.action.API_WRITE_CANCEL"
+    }
+
+    abstract fun getSendProgress(usbDevice: UsbDevice, uri: Uri): (Long) -> Unit
+    abstract fun getInputStream(uri: Uri): InputStream
+
+    private fun getUsbMSDevice(usbDevice: UsbDevice): UsbMassStorageDevice? {
+        val msDevs = UsbMassStorageDevice.getMassStorageDevices(this)
+
+        for (dev in msDevs) {
+            if (dev.usbDevice == usbDevice)
+                return dev
+        }
+
+        return null
+    }
+
+    fun writeInputStream(inputStream: InputStream, msDev: UsbMassStorageDevice, sendProgress: (Long) -> Unit): Long {
+        val blockDev = msDev.blockDevice
+        val bsFactor = DD_BLOCKSIZE / blockDev.blockSize
+        val buffIS = BufferedInputStream(inputStream)
+        val byteBuffer = ByteBuffer.allocate(blockDev.blockSize * bsFactor)
+
+        var lastReadBytes: Int
+        var readBytes = 0
+        var readBlocksBytes = 0
+        var offset = 0L
+        var writtenBytes: Long = 0
+        var remaining = 0
+
+        while (true) {
+            wakeLock(true)
+            lastReadBytes = buffIS.read(byteBuffer.array()!!, remaining, byteBuffer.array().size - remaining)
+            if (lastReadBytes < 0 && readBytes > 0) {
+                // EOF, pad with some extra bits until next block
+                if (readBytes % blockDev.blockSize > 0)
+                    readBytes += blockDev.blockSize - (readBytes % blockDev.blockSize)
+            } else if (lastReadBytes < 0) {
+                // EOF, we've already written everything
+                break
+            } else {
+                readBytes += lastReadBytes
+            }
+
+            byteBuffer.position(0)
+
+            // Ensure written content size is a multiple of the block size
+            remaining = readBytes % blockDev.blockSize
+            readBlocksBytes = readBytes - remaining
+            byteBuffer.limit(readBlocksBytes)
+
+            // Write the buffer to the device
+            blockDev.write(offset, byteBuffer)
+            offset += (readBlocksBytes) / blockDev.blockSize
+            writtenBytes += readBlocksBytes
+
+            // Copy remaining bytes to the beginning of the buffer
+            for (i in 0 until remaining)
+                byteBuffer.array()[i] = byteBuffer.array()[readBlocksBytes + i]
+
+            readBytes = remaining
+
+            sendProgress(writtenBytes)
+        }
+
+        return writtenBytes
+    }
+
+    override fun writeImage(intent: Intent): Long {
+        val uri: Uri = intent.data!!
+        val inputStream = getInputStream(uri)
+
+        val usbDevice: UsbDevice = intent.getParcelableExtra("usbDevice")
+        val msDev = getUsbMSDevice(usbDevice)!!
+        msDev.init()
+
+        val sendProgress = getSendProgress(usbDevice, uri)
+        val startTime = System.currentTimeMillis()
+        var writtenBytes: Long = 0
+
+
+        try {
+            writtenBytes = writeInputStream(inputStream, msDev, sendProgress)
+
+            resultNotification(usbDevice.name, uri.getFileName(this)!!, true, writtenBytes, startTime)
+        } catch (e: Exception) {
+            resultNotification(usbDevice.name, uri.getFileName(this)!!, false, writtenBytes, startTime)
+            Log.e(TAG, "Could't write image to ${usbDevice.name}")
+            throw e
+        } finally {
+            wakeLock(false)
+            msDev.close()
+        }
+
+        Log.d(TAG, "Written $writtenBytes bytes to ${usbDevice.name} using API")
+        return writtenBytes
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
index 723220f..094c362 100644
--- a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
+++ b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
@@ -12,6 +12,7 @@ import android.support.v4.app.NotificationCompat
 import eu.depau.etchdroid.R
 import eu.depau.etchdroid.kotlin_exts.toHRSize
 import eu.depau.etchdroid.kotlin_exts.toHRTime
+import kotlin.math.max
 
 
 abstract class UsbWriteService(name: String) : IntentService(name) {
@@ -30,7 +31,7 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
     private val WL_TIMEOUT = 10 * 60 * 1000L
 
     override fun onHandleIntent(intent: Intent?) {
-        startForeground(FOREGROUND_ID, buildForegroundNotification(null, null, -1, -1))
+        startForeground(FOREGROUND_ID, buildForegroundNotification(null, null, -1))
 
         try {
             writeImage(intent!!)
@@ -73,20 +74,18 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
             NotificationCompat.Builder(this)
     }
 
-    fun updateNotification(usbDevice: String, filename: String?, bytes: Long, total: Long) {
+    fun updateNotification(usbDevice: String, filename: String?, bytes: Long, progr: Int) {
         // Notification rate limiting
         val time = System.currentTimeMillis()
         if (time <= prevTime + 1000)
             return
 
-        val speed = ((bytes - prevBytes).toDouble() / (time - prevTime).toDouble() * 1000).toHRSize()
+        val speed = max((bytes - prevBytes).toDouble() / (time - prevTime).toDouble() * 1000, 0.0).toHRSize()
         prevTime = time
         prevBytes = bytes
 
-        val perc: Int = (bytes.toDouble() / total * 100.0).toInt()
-
         val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
-        notificationManager.notify(FOREGROUND_ID, buildForegroundNotification(usbDevice, filename, bytes, total, "$perc% • $speed/s"))
+        notificationManager.notify(FOREGROUND_ID, buildForegroundNotification(usbDevice, filename, progr, "$progr% • $speed/s"))
     }
 
     fun resultNotification(usbDevice: String, filename: String, success: Boolean, bytes: Long = 0, startTime: Long = 0) {
@@ -102,7 +101,7 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
                     .setContentText("$usbDevice may have been unplugged while writing.")
                     .setSubText(dt.toHRTime())
         else {
-            val speed = (bytes.toDouble() / dt.toDouble() * 1000).toHRSize() + "/s"
+            val speed = max(bytes.toDouble() / dt.toDouble() * 1000, 0.0).toHRSize() + "/s"
             b.setContentTitle("Write finished")
                     .setContentText("$filename successfully written to $usbDevice")
                     .setSubText("${dt.toHRTime()} • ${bytes.toHRSize()} • $speed")
@@ -114,23 +113,23 @@ abstract class UsbWriteService(name: String) : IntentService(name) {
         notificationManager.notify(RESULT_NOTIFICATION_ID, b.build())
     }
 
-    fun buildForegroundNotification(usbDevice: String?, filename: String?, bytes: Long, total: Long, subText: String? = null): Notification {
-        val progr: Int
+    fun buildForegroundNotification(usbDevice: String?, filename: String?, progr: Int, subText: String? = null, title: String = getString(R.string.notif_writing_img)): Notification {
         val indet: Boolean
+        val prog: Int
 
-        if (total < 0) {
-            progr = 0
+        if (progr < 0) {
+            prog = 0
             indet = true
         } else {
-            progr = (bytes.toFloat() / total * 100).toInt()
+            prog = progr
             indet = false
         }
 
         val b = getNotificationBuilder()
 
-        b.setContentTitle(getString(R.string.notif_writing_img))
+        b.setContentTitle(title)
                 .setOngoing(true)
-                .setProgress(100, progr, indet)
+                .setProgress(100, prog, indet)
 
         if (usbDevice != null && filename != null)
             b.setContentText("${filename} to $usbDevice")
diff --git a/dmg2img/src/c/dmg2img b/dmg2img/src/c/dmg2img
index 1d75292..fd3763b 160000
--- a/dmg2img/src/c/dmg2img
+++ b/dmg2img/src/c/dmg2img
@@ -1 +1 @@
-Subproject commit 1d7529285a7bdeb4f1eb158d9305494d682e323d
+Subproject commit fd3763bb07aa06c9b56b3f6d632d475d59f41433