Improve progress bar handling for backup / restore / image flash

The progress bar will now be updated during image backups, restores
and during image flashing (except for sparse images which will require
significant changes to libsparse, and except for mtd nand using
flash_utils).

The progress bar will now be updated mid-file for file systems (tar) so
the user will see changes even during large file backup / restore.

Add a new progress tracking class to simplify handling of progress bar
updates. The class will only update the progress bar 5 times a second to
reduce the CPU load from updating the GUI frequently which does affect
backup times.

Change-Id: Iff382faef3df1f86604af336c1a8ce8993cd12c5
diff --git a/Android.mk b/Android.mk
index 08dbf6e..b2c06e8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,7 @@
     data.cpp \
     partition.cpp \
     partitionmanager.cpp \
+    progresstracking.cpp \
     twinstall.cpp \
     twrp-functions.cpp \
     openrecoveryscript.cpp \
diff --git a/gui/twmsg.cpp b/gui/twmsg.cpp
index 9fe9e5a..57f1922 100644
--- a/gui/twmsg.cpp
+++ b/gui/twmsg.cpp
@@ -89,13 +89,16 @@
 			resname = name.substr(0, pos);
 			default_value = name.substr(pos + 1);
 		}
+#ifndef BUILD_TWRPTAR_MAIN
 		const ResourceManager* res = PageManager::GetResources();
 		if (res) {
 			if (default_value.empty())
 				return res->FindString(resname);
 			else
 				return res->FindString(resname, default_value);
-		} else if (!default_value.empty()) {
+		}
+#endif
+		if (!default_value.empty()) {
 			return default_value;
 		}
 		return name;
@@ -112,10 +115,12 @@
 public:
 	virtual std::string operator()(const std::string& name) const
 	{
+#ifndef BUILD_TWRPTAR_MAIN
 		std::string value;
 		if (DataManager::GetValue(name, value) == 0)
 			return value;
 		else
+#endif
 			return "";
 	}
 };
diff --git a/libtar/extract.c b/libtar/extract.c
index 257e140..6a63ff7 100644
--- a/libtar/extract.c
+++ b/libtar/extract.c
@@ -32,6 +32,8 @@
 # include "selinux/selinux.h"
 #endif
 
+const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE);
+
 static int
 tar_set_file_perms(TAR *t, const char *realname)
 {
@@ -245,6 +247,11 @@
 			close(fdout);
 			return -1;
 		}
+		else
+		{
+			if (*progress_fd != 0)
+				write(*progress_fd, &progress_size, sizeof(progress_size));
+		}
 	}
 
 	/* close output file */
@@ -255,12 +262,6 @@
 	printf("### done extracting %s\n", filename);
 #endif
 
-	if (*progress_fd != 0)
-	{
-		unsigned long long file_size = (unsigned long long)(size);
-		write(*progress_fd, &file_size, sizeof(file_size));
-	}
-
 	return 0;
 }
 
diff --git a/partition.cpp b/partition.cpp
index 188a739..12d4ad0 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2013 TeamWin
+	Copyright 2013 to 2016 TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -28,6 +28,7 @@
 #include <iostream>
 #include <sstream>
 #include <sys/param.h>
+#include <fcntl.h>
 
 #ifdef TW_INCLUDE_CRYPTO
 	#include "cutils/properties.h"
@@ -69,6 +70,7 @@
 #include <linux/xattr.h>
 #endif
 #include <sparse_format.h>
+#include "progresstracking.hpp"
 
 using namespace std;
 
@@ -1466,14 +1468,14 @@
 	return false;
 }
 
-bool TWPartition::Backup(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid) {
+bool TWPartition::Backup(const string& backup_folder, pid_t &tar_fork_pid, ProgressTracking *progress) {
 	if (Backup_Method == FILES) {
-		return Backup_Tar(backup_folder, overall_size, other_backups_size, tar_fork_pid);
+		return Backup_Tar(backup_folder, progress, tar_fork_pid);
 	}
 	else if (Backup_Method == DD)
-		return Backup_DD(backup_folder);
+		return Backup_Image(backup_folder, progress);
 	else if (Backup_Method == FLASH_UTILS)
-		return Backup_Dump_Image(backup_folder);
+		return Backup_Dump_Image(backup_folder, progress);
 	LOGERR("Unknown backup method for '%s'\n", Mount_Point.c_str());
 	return false;
 }
@@ -1526,7 +1528,7 @@
 	return false;
 }
 
-bool TWPartition::Restore(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+bool TWPartition::Restore(const string& restore_folder, ProgressTracking *progress) {
 	string Restore_File_System;
 
 	TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, gui_parse_text("{@restoring_hdr}"));
@@ -1535,16 +1537,16 @@
 	Restore_File_System = Get_Restore_File_System(restore_folder);
 
 	if (Is_File_System(Restore_File_System))
-		return Restore_Tar(restore_folder, Restore_File_System, total_restore_size, already_restored_size);
+		return Restore_Tar(restore_folder, Restore_File_System, progress);
 	else if (Is_Image(Restore_File_System)) {
-		return Restore_Image(restore_folder, total_restore_size, already_restored_size, Restore_File_System);
+		return Restore_Image(restore_folder, Restore_File_System, progress);
 	}
 
 	LOGERR("Unknown restore method for '%s'\n", Mount_Point.c_str());
 	return false;
 }
 
-string TWPartition::Get_Restore_File_System(string restore_folder) {
+string TWPartition::Get_Restore_File_System(const string& restore_folder) {
 	size_t first_period, second_period;
 	string Restore_File_System;
 
@@ -2009,14 +2011,9 @@
 	return false;
 }
 
-bool TWPartition::Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid) {
-	char back_name[255], split_index[5];
-	string Full_FileName, Split_FileName, Tar_Args, Command;
-	int use_compression, use_encryption = 0, index, backup_count;
-	struct stat st;
-	unsigned long long total_bsize = 0, file_size;
+bool TWPartition::Backup_Tar(const string& backup_folder, ProgressTracking *progress, pid_t &tar_fork_pid) {
+	string Full_FileName;
 	twrpTar tar;
-	vector <string> files;
 
 	if (!Mount(true))
 		return false;
@@ -2024,25 +2021,24 @@
 	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Backup_Display_Name, "Backing Up");
 	gui_msg(Msg("backing_up=Backing up {1}...")(Backup_Display_Name));
 
-	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
-	tar.use_compression = use_compression;
+	DataManager::GetValue(TW_USE_COMPRESSION_VAR, tar.use_compression);
 
 #ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
-	DataManager::GetValue("tw_encrypt_backup", use_encryption);
-	if (use_encryption && Can_Encrypt_Backup) {
-		tar.use_encryption = use_encryption;
-		if (Use_Userdata_Encryption)
-			tar.userdata_encryption = use_encryption;
-		string Password;
-		DataManager::GetValue("tw_backup_password", Password);
-		tar.setpassword(Password);
-	} else {
-		use_encryption = false;
+	if (Can_Encrypt_Backup) {
+		DataManager::GetValue("tw_encrypt_backup", tar.use_encryption);
+		if (tar.use_encryption) {
+			if (Use_Userdata_Encryption)
+				tar.userdata_encryption = tar.use_encryption;
+			string Password;
+			DataManager::GetValue("tw_backup_password", Password);
+			tar.setpassword(Password);
+		} else {
+			tar.use_encryption = 0;
+		}
 	}
 #endif
 
-	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
-	Backup_FileName = back_name;
+	Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
 	Full_FileName = backup_folder + "/" + Backup_FileName;
 	tar.has_data_media = Has_Data_Media;
 	Full_FileName = backup_folder + "/" + Backup_FileName;
@@ -2051,39 +2047,23 @@
 	tar.setsize(Backup_Size);
 	tar.partition_name = Backup_Name;
 	tar.backup_folder = backup_folder;
-	if (tar.createTarFork(overall_size, other_backups_size, tar_fork_pid) != 0)
+	if (tar.createTarFork(progress, tar_fork_pid) != 0)
 		return false;
 	return true;
 }
 
-bool TWPartition::Backup_DD(string backup_folder) {
-	char back_name[255], block_size[32], dd_count[32];
-	string Full_FileName, Command, DD_BS, DD_COUNT;
-	int use_compression;
-	unsigned long long DD_Block_Size, DD_Count;
-
-	DD_Block_Size = 16 * 1024 * 1024;
-	while (Backup_Size % DD_Block_Size != 0) DD_Block_Size >>= 1;
-
-	DD_Count = Backup_Size / DD_Block_Size;
-
-	sprintf(dd_count, "%llu", DD_Count);
-	DD_COUNT = dd_count;
-
-	sprintf(block_size, "%llu", DD_Block_Size);
-	DD_BS = block_size;
+bool TWPartition::Backup_Image(const string& backup_folder, ProgressTracking *progress) {
+	string Full_FileName;
 
 	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, gui_parse_text("{@backing}"));
 	gui_msg(Msg("backing_up=Backing up {1}...")(Backup_Display_Name));
 
-	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
-	Backup_FileName = back_name;
-
+	Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
 	Full_FileName = backup_folder + "/" + Backup_FileName;
 
-	Command = "dd if=" + Actual_Block_Device + " of='" + Full_FileName + "'" + " bs=" + DD_BS + " count=" + DD_COUNT;
-	LOGINFO("Backup command: '%s'\n", Command.c_str());
-	TWFunc::Exec_Cmd(Command);
+	if (!Raw_Read_Write(Actual_Block_Device, Full_FileName, Backup_Size, progress))
+		return false;
+
 	tw_set_default_metadata(Full_FileName.c_str());
 	if (TWFunc::Get_File_Size(Full_FileName) == 0) {
 		gui_msg(Msg(msg::kError, "backup_size=Backup file size for '{1}' is 0 bytes.")(Full_FileName));
@@ -2092,17 +2072,77 @@
 	return true;
 }
 
-bool TWPartition::Backup_Dump_Image(string backup_folder) {
-	char back_name[255];
+bool TWPartition::Raw_Read_Write(const string& input_file, const string& output_file, const unsigned long long input_size, ProgressTracking *progress) {
+	unsigned long long RW_Block_Size, Remain;
+	int src_fd = -1, dest_fd = -1, bs;
+	bool ret = false;
+	void* buffer = NULL;
+	unsigned long long backedup_size = 0;
+
+	RW_Block_Size = 1048576LLU; // 1MB
+	Remain = input_size;
+
+	src_fd = open(input_file.c_str(), O_RDONLY | O_LARGEFILE);
+	if (src_fd < 0) {
+		gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(input_file)(strerror(errno)));
+		return false;
+	}
+	dest_fd = open(output_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR);
+	if (dest_fd < 0) {
+		gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(output_file)(strerror(errno)));
+		goto exit;
+	}
+	bs = (int)(RW_Block_Size);
+	buffer = malloc((size_t)bs);
+	if (!buffer) {
+		LOGINFO("Raw_Read_Write failed to malloc\n");
+		goto exit;
+	}
+	LOGINFO("Reading '%s', writing '%s'\n", input_file.c_str(), output_file.c_str());
+	if (progress)
+		progress->SetPartitionSize(input_size);
+	while (Remain > 0) {
+		if (Remain < RW_Block_Size)
+			bs = (int)(Remain);
+		if (read(src_fd, buffer, bs) != bs) {
+			LOGINFO("Error reading source fd (%s)\n", strerror(errno));
+			goto exit;
+		}
+		if (write(dest_fd, buffer, bs) != bs) {
+			LOGINFO("Error writing destination fd (%s)\n", strerror(errno));
+			goto exit;
+		}
+		backedup_size += (unsigned long long)(bs);
+		Remain -= (unsigned long long)(bs);
+		if (progress)
+			progress->UpdateSize(backedup_size);
+		if (PartitionManager.Check_Backup_Cancel() != 0)
+			goto exit;
+	}
+	if (progress)
+		progress->UpdateDisplayDetails(true);
+	fsync(dest_fd);
+	ret = true;
+exit:
+	if (src_fd >= 0)
+		close(src_fd);
+	if (dest_fd >= 0)
+		close(dest_fd);
+	if (buffer)
+		free(buffer);
+	return ret;
+}
+
+bool TWPartition::Backup_Dump_Image(const string& backup_folder, ProgressTracking *progress) {
 	string Full_FileName, Command;
-	int use_compression;
 
 	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, gui_parse_text("{@backing}"));
 	gui_msg(Msg("backing_up=Backing up {1}...")(Backup_Display_Name));
 
-	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
-	Backup_FileName = back_name;
+	if (progress)
+		progress->SetPartitionSize(Backup_Size);
 
+	Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
 	Full_FileName = backup_folder + "/" + Backup_FileName;
 
 	Command = "dump_image " + MTD_Name + " '" + Full_FileName + "'";
@@ -2114,10 +2154,12 @@
 		gui_msg(Msg(msg::kError, "backup_size=Backup file size for '{1}' is 0 bytes.")(Full_FileName));
 		return false;
 	}
+	if (progress)
+		progress->UpdateSize(Backup_Size);
 	return true;
 }
 
-unsigned long long TWPartition::Get_Restore_Size(string restore_folder) {
+unsigned long long TWPartition::Get_Restore_Size(const string& restore_folder) {
 	InfoManager restore_info(restore_folder + "/" + Backup_Name + ".info");
 	if (restore_info.LoadValues() == 0) {
 		if (restore_info.GetValue("backup_size", Restore_Size) == 0) {
@@ -2150,10 +2192,8 @@
 	return Restore_Size;
 }
 
-bool TWPartition::Restore_Tar(string restore_folder, string Restore_File_System, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
-	string Full_FileName, Command;
-	int index = 0;
-	char split_index[5];
+bool TWPartition::Restore_Tar(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress) {
+	string Full_FileName;
 	bool ret = false;
 
 	if (Has_Android_Secure) {
@@ -2188,7 +2228,8 @@
 	if (!Password.empty())
 		tar.setpassword(Password);
 #endif
-	if (tar.extractTarFork(total_restore_size, already_restored_size) != 0)
+	progress->SetPartitionSize(Get_Restore_Size(restore_folder));
+	if (tar.extractTarFork(progress) != 0)
 		ret = false;
 	else
 		ret = true;
@@ -2218,28 +2259,21 @@
 	return ret;
 }
 
-bool TWPartition::Restore_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size, string Restore_File_System) {
+bool TWPartition::Restore_Image(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress) {
 	string Full_FileName;
-	double display_percent, progress_percent;
-	char size_progress[1024];
 
 	TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Backup_Display_Name, gui_parse_text("{@restoring_hdr}"));
 	gui_msg(Msg("restoring=Restoring {1}...")(Backup_Display_Name));
 	Full_FileName = restore_folder + "/" + Backup_FileName;
 
 	if (Restore_File_System == "emmc") {
-		if (!Flash_Image_DD(Full_FileName))
+		unsigned long long file_size = (unsigned long long)(TWFunc::Get_File_Size(Full_FileName));
+		if (!Raw_Read_Write(Full_FileName, Actual_Block_Device, file_size, progress))
 			return false;
 	} else if (Restore_File_System == "mtd" || Restore_File_System == "bml") {
-		if (!Flash_Image_FI(Full_FileName))
+		if (!Flash_Image_FI(Full_FileName, progress))
 			return false;
 	}
-	display_percent = (double)(Restore_Size + *already_restored_size) / (double)(*total_restore_size) * 100;
-	sprintf(size_progress, "%lluMB of %lluMB, %i%%", (Restore_Size + *already_restored_size) / 1048576, *total_restore_size / 1048576, (int)(display_percent));
-	DataManager::SetValue("tw_size_progress", size_progress);
-	progress_percent = (display_percent / 100);
-	DataManager::SetProgress((float)(progress_percent));
-	*already_restored_size += Restore_Size;
 	return true;
 }
 
@@ -2380,7 +2414,7 @@
 	return maxFileSize - 1;
 }
 
-bool TWPartition::Flash_Image(string Filename) {
+bool TWPartition::Flash_Image(const string& Filename) {
 	string Restore_File_System;
 
 	LOGINFO("Image filename is: %s\n", Filename.c_str());
@@ -2403,21 +2437,23 @@
 			gui_err("img_size_err=Size of image is larger than target device");
 			return false;
 		}
-		if (Backup_Method == DD)
-			return Flash_Image_DD(Filename);
-		else if (Backup_Method == FLASH_UTILS)
-			return Flash_Image_FI(Filename);
+		if (Backup_Method == DD) {
+			if (Is_Sparse_Image(Filename)) {
+				return Flash_Sparse_Image(Filename);
+			}
+			unsigned long long file_size = (unsigned long long)(TWFunc::Get_File_Size(Filename));
+			ProgressTracking pt(file_size);
+			return Raw_Read_Write(Filename, Actual_Block_Device, file_size, &pt);
+		} else if (Backup_Method == FLASH_UTILS) {
+			return Flash_Image_FI(Filename, NULL);
+		}
 	}
 
 	LOGERR("Unknown flash method for '%s'\n", Mount_Point.c_str());
 	return false;
 }
 
-bool TWPartition::Flash_Image_DD(string Filename) {
-	string Command;
-
-	gui_msg(Msg("flashing=Flashing {1}...")(Display_Name));
-
+bool TWPartition::Is_Sparse_Image(const string& Filename) {
 	uint32_t magic = 0;
 	int fd = open(Filename.c_str(), O_RDONLY);
 	if (fd < 0) {
@@ -2430,20 +2466,31 @@
 		return false;
 	}
 	close(fd);
-	if (magic == SPARSE_HEADER_MAGIC) {
-		Command = "simg2img '" + Filename + "' " + Actual_Block_Device;
-	} else {
-		Command = "dd bs=8388608 if='" + Filename + "' of=" + Actual_Block_Device;
-	}
+	if (magic == SPARSE_HEADER_MAGIC)
+		return true;
+	return false;
+}
+
+bool TWPartition::Flash_Sparse_Image(const string& Filename) {
+	string Command;
+
+	gui_msg(Msg("flashing=Flashing {1}...")(Display_Name));
+
+	Command = "simg2img '" + Filename + "' '" + Actual_Block_Device + "'";
 	LOGINFO("Flash command: '%s'\n", Command.c_str());
 	TWFunc::Exec_Cmd(Command);
 	return true;
 }
 
-bool TWPartition::Flash_Image_FI(string Filename) {
+bool TWPartition::Flash_Image_FI(const string& Filename, ProgressTracking *progress) {
 	string Command;
+	unsigned long long file_size;
 
 	gui_msg(Msg("flashing=Flashing {1}...")(Display_Name));
+	if (progress) {
+		file_size = (unsigned long long)(TWFunc::Get_File_Size(Filename));
+		progress->SetPartitionSize(file_size);
+	}
 	// Sometimes flash image doesn't like to flash due to the first 2KB matching, so we erase first to ensure that it flashes
 	Command = "erase_image " + MTD_Name;
 	LOGINFO("Erase command: '%s'\n", Command.c_str());
@@ -2451,6 +2498,8 @@
 	Command = "flash_image " + MTD_Name + " '" + Filename + "'";
 	LOGINFO("Flash command: '%s'\n", Command.c_str());
 	TWFunc::Exec_Cmd(Command);
+	if (progress)
+		progress->UpdateSize(file_size);
 	return true;
 }
 
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 1c7af5e..0034fea 100644
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2014 TeamWin
+	Copyright 2014 to 2016 TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -43,6 +43,7 @@
 #include "set_metadata.h"
 #include "tw_atomic.hpp"
 #include "gui/gui.hpp"
+#include "progresstracking.hpp"
 
 #ifdef TW_HAS_MTP
 #include "mtp/mtp_MtpServer.hpp"
@@ -527,11 +528,9 @@
 	return true;
 }
 
-bool TWPartitionManager::Backup_Partition(TWPartition* Part, string Backup_Folder, bool generate_md5, unsigned long long* img_bytes_remaining, unsigned long long* file_bytes_remaining, unsigned long *img_time, unsigned long *file_time, unsigned long long *img_bytes, unsigned long long *file_bytes) {
+bool TWPartitionManager::Backup_Partition(TWPartition* Part, const string& Backup_Folder, bool generate_md5, unsigned long *img_time, unsigned long *file_time, ProgressTracking *progress) {
 	time_t start, stop;
 	int use_compression;
-	float pos;
-	unsigned long long total_size, current_size;
 
 	string backup_log = Backup_Folder + "recovery.log";
 
@@ -540,26 +539,17 @@
 
 	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
 
-	total_size = *file_bytes + *img_bytes;
-	current_size = *file_bytes + *img_bytes - *file_bytes_remaining - *img_bytes_remaining;
-	// Set the position
-	pos = ((float)(current_size) / (float)(total_size));
-	DataManager::SetProgress(pos);
-
 	TWFunc::SetPerformanceMode(true);
 	time(&start);
 
-	if (Part->Backup(Backup_Folder, &total_size, &current_size, tar_fork_pid)) {
+	if (Part->Backup(Backup_Folder, tar_fork_pid, progress)) {
 		bool md5Success = false;
-		current_size += Part->Backup_Size;
-		pos = (float)((float)(current_size) / (float)(total_size));
-		DataManager::SetProgress(pos);
 		if (Part->Has_SubPartition) {
 			std::vector<TWPartition*>::iterator subpart;
 
 			for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
 				if ((*subpart)->Can_Be_Backed_Up && (*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == Part->Mount_Point) {
-					if (!(*subpart)->Backup(Backup_Folder, &total_size, &current_size, tar_fork_pid)) {
+					if (!(*subpart)->Backup(Backup_Folder, tar_fork_pid, progress)) {
 						TWFunc::SetPerformanceMode(false);
 						Clean_Backup_Folder(Backup_Folder);
 						TWFunc::copy_file("/tmp/recovery.log", backup_log, 0644);
@@ -572,14 +562,6 @@
 						TWFunc::SetPerformanceMode(false);
 						return false;
 					}
-					if (Part->Backup_Method == 1) {
-						*file_bytes_remaining -= (*subpart)->Backup_Size;
-					} else {
-						*img_bytes_remaining -= (*subpart)->Backup_Size;
-					}
-					current_size += Part->Backup_Size;
-					pos = (float)(current_size / total_size);
-					DataManager::SetProgress(pos);
 				}
 			}
 		}
@@ -587,10 +569,8 @@
 		int backup_time = (int) difftime(stop, start);
 		LOGINFO("Partition Backup time: %d\n", backup_time);
 		if (Part->Backup_Method == 1) {
-			*file_bytes_remaining -= Part->Backup_Size;
 			*file_time += backup_time;
 		} else {
-			*img_bytes_remaining -= Part->Backup_Size;
 			*img_time += backup_time;
 		}
 
@@ -636,6 +616,10 @@
 	closedir(d);
 }
 
+int TWPartitionManager::Check_Backup_Cancel() {
+	return stop_backup.get_value();
+}
+
 int TWPartitionManager::Cancel_Backup() {
 	string Backup_Folder, Backup_Name, Full_Backup_Path;
 
@@ -738,6 +722,7 @@
 		return false;
 	}
 	total_bytes = file_bytes + img_bytes;
+	ProgressTracking progress(total_bytes);
 	gui_msg(Msg("total_partitions_backup= * Total number of partitions to back up: {1}")(partition_count));
 	gui_msg(Msg("total_backup_size= * Total size of all data: {1}MB")(total_bytes / 1024 / 1024));
 	storage = Find_Partition_By_Path(DataManager::GetCurrentStoragePath());
@@ -777,7 +762,7 @@
 		backup_path = Backup_List.substr(start_pos, end_pos - start_pos);
 		backup_part = Find_Partition_By_Path(backup_path);
 		if (backup_part != NULL) {
-			if (!Backup_Partition(backup_part, Full_Backup_Path, do_md5, &img_bytes_remaining, &file_bytes_remaining, &img_time, &file_time, &img_bytes, &file_bytes))
+			if (!Backup_Partition(backup_part, Full_Backup_Path, do_md5, &img_time, &file_time, &progress))
 				return false;
 		} else {
 			gui_msg(Msg(msg::kError, "unable_to_locate_partition=Unable to locate '{1}' partition for backup calculations.")(backup_path));
@@ -794,8 +779,10 @@
 	int img_bps = (int)img_bytes / (int)img_time;
 	unsigned long long file_bps = file_bytes / (int)file_time;
 
-	gui_msg(Msg("avg_backup_fs=Average backup rate for file systems: {1} MB/sec")(file_bps / (1024 * 1024)));
-	gui_msg(Msg("avg_backup_img=Average backup rate for imaged drives: {1} MB/sec")(img_bps / (1024 * 1024)));
+	if (file_bytes != 0)
+		gui_msg(Msg("avg_backup_fs=Average backup rate for file systems: {1} MB/sec")(file_bps / (1024 * 1024)));
+	if (img_bytes != 0)
+		gui_msg(Msg("avg_backup_img=Average backup rate for imaged drives: {1} MB/sec")(img_bps / (1024 * 1024)));
 
 	time(&total_stop);
 	int total_time = (int) difftime(total_stop, total_start);
@@ -832,12 +819,12 @@
 	return true;
 }
 
-bool TWPartitionManager::Restore_Partition(TWPartition* Part, string Restore_Name, int partition_count, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+bool TWPartitionManager::Restore_Partition(TWPartition* Part, const string& Restore_Name, ProgressTracking *progress) {
 	time_t Start, Stop;
 	TWFunc::SetPerformanceMode(true);
 	time(&Start);
-	//DataManager::ShowProgress(1.0 / (float)partition_count, 150);
-	if (!Part->Restore(Restore_Name, total_restore_size, already_restored_size)) {
+
+	if (!Part->Restore(Restore_Name, progress)) {
 		TWFunc::SetPerformanceMode(false);
 		return false;
 	}
@@ -846,7 +833,7 @@
 
 		for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
 			if ((*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == Part->Mount_Point) {
-				if (!(*subpart)->Restore(Restore_Name, total_restore_size, already_restored_size)) {
+				if (!(*subpart)->Restore(Restore_Name, progress)) {
 					TWFunc::SetPerformanceMode(false);
 					return false;
 				}
@@ -859,7 +846,7 @@
 	return true;
 }
 
-int TWPartitionManager::Run_Restore(string Restore_Name) {
+int TWPartitionManager::Run_Restore(const string& Restore_Name) {
 	int check_md5, check, partition_count = 0;
 	TWPartition* restore_part = NULL;
 	time_t rStart, rStop;
@@ -925,6 +912,7 @@
 	gui_msg(Msg("restore_part_count=Restoring {1} partitions...")(partition_count));
 	gui_msg(Msg("total_restore_size=Total restore size is {1}MB")(total_restore_size / 1048576));
 	DataManager::SetProgress(0.0);
+	ProgressTracking progress(total_restore_size);
 
 	start_pos = 0;
 	if (!Restore_List.empty()) {
@@ -934,7 +922,7 @@
 			restore_part = Find_Partition_By_Path(restore_path);
 			if (restore_part != NULL) {
 				partition_count++;
-				if (!Restore_Partition(restore_part, Restore_Name, partition_count, &total_restore_size, &already_restored_size))
+				if (!Restore_Partition(restore_part, Restore_Name, &progress))
 					return false;
 			} else {
 				gui_msg(Msg(msg::kError, "restore_unable_locate=Unable to locate '{1}' partition for restoring.")(restore_path));
diff --git a/partitions.hpp b/partitions.hpp
index 1d43e04..035acf0 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2014 TeamWin
+	Copyright 2014 to 2016 TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -23,6 +23,7 @@
 #include <string>
 #include "twrpDU.hpp"
 #include "tw_atomic.hpp"
+#include "progresstracking.hpp"
 
 #define MAX_FSTAB_LINE_LENGTH 2048
 
@@ -64,17 +65,17 @@
 	bool Repair();                                                            // Repairs the current file system
 	bool Can_Resize();                                                        // Checks to see if we have everything needed to be able to resize the current file system
 	bool Resize();                                                            // Resizes the current file system
-	bool Backup(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid); // Backs up the partition to the folder specified
+	bool Backup(const string& backup_folder, pid_t &tar_fork_pid, ProgressTracking *progress); // Backs up the partition to the folder specified
 	bool Check_MD5(string restore_folder);                                    // Checks MD5 of a backup
-	bool Restore(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restores the partition using the backup folder provided
-	unsigned long long Get_Restore_Size(string restore_folder);               // Returns the overall restore size of the backup
+	bool Restore(const string& restore_folder, ProgressTracking *progress);   // Restores the partition using the backup folder provided
+	unsigned long long Get_Restore_Size(const string& restore_folder);        // Returns the overall restore size of the backup
 	string Backup_Method_By_Name();                                           // Returns a string of the backup method for human readable output
 	bool Decrypt(string Password);                                            // Decrypts the partition, return 0 for failure and -1 for success
 	bool Wipe_Encryption();                                                   // Ignores wipe commands for /data/media devices and formats the original block device
 	void Check_FS_Type();                                                     // Checks the fs type using blkid, does not do anything on MTD / yaffs2 because this crashes on some devices
 	bool Update_Size(bool Display_Error);                                     // Updates size information
 	void Recreate_Media_Folder();                                             // Recreates the /data/media folder
-	bool Flash_Image(string Filename);                                        // Flashes an image to the partition
+	bool Flash_Image(const string& Filename);                                        // Flashes an image to the partition
 	void Change_Mount_Read_Only(bool new_value);                              // Changes Mount_Read_Only to new_value
 	int Check_Lifetime_Writes();
 	int Decrypt_Adopted();
@@ -118,20 +119,22 @@
 	bool Wipe_NTFS();                                                         // Uses mkntfs to wipe
 	bool Wipe_Data_Without_Wiping_Media();                                    // Uses rm -rf to wipe but does not wipe /data/media
 	bool Wipe_Data_Without_Wiping_Media_Func(const string& parent);           // Uses rm -rf to wipe but does not wipe /data/media
-	bool Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid); // Backs up using tar for file systems
-	bool Backup_DD(string backup_folder);                                     // Backs up using dd for emmc memory types
-	bool Backup_Dump_Image(string backup_folder);                             // Backs up using dump_image for MTD memory types
-	string Get_Restore_File_System(string restore_folder);                    // Returns the file system that was in place at the time of the backup
-	bool Restore_Tar(string restore_folder, string Restore_File_System, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restore using tar for file systems
-	bool Restore_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size, string Restore_File_System); // Restore using dd for images
+	bool Backup_Tar(const string& backup_folder, ProgressTracking *progress, pid_t &tar_fork_pid); // Backs up using tar for file systems
+	bool Backup_Image(const string& backup_folder, ProgressTracking *progress); // Backs up using raw read/write for emmc memory types
+	bool Raw_Read_Write(const string& input_file, const string& output_file, const unsigned long long input_size, ProgressTracking *progress);
+	bool Backup_Dump_Image(const string& backup_folder, ProgressTracking *progress); // Backs up using dump_image for MTD memory types
+	string Get_Restore_File_System(const string& restore_folder);             // Returns the file system that was in place at the time of the backup
+	bool Restore_Tar(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress); // Restore using tar for file systems
+	bool Restore_Image(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress); // Restore using raw read/write for images
 	bool Get_Size_Via_statfs(bool Display_Error);                             // Get Partition size, used, and free space using statfs
 	bool Get_Size_Via_df(bool Display_Error);                                 // Get Partition size, used, and free space using df command
 	bool Make_Dir(string Path, bool Display_Error);                           // Creates a directory if it doesn't already exist
 	bool Find_MTD_Block_Device(string MTD_Name);                              // Finds the mtd block device based on the name from the fstab
 	void Recreate_AndSec_Folder(void);                                        // Recreates the .android_secure folder
 	void Mount_Storage_Retry(void);                                           // Tries multiple times with a half second delay to mount a device in case storage is slow to mount
-	bool Flash_Image_DD(string Filename);                                     // Flashes an image to the partition using dd
-	bool Flash_Image_FI(string Filename);                                     // Flashes an image to the partition using flash_image for mtd nand
+	bool Is_Sparse_Image(const string& Filename);                             // Determines if a file is in sparse image format
+	bool Flash_Sparse_Image(const string& Filename);                          // Flashes a sparse image using simg2img
+	bool Flash_Image_FI(const string& Filename, ProgressTracking *progress);  // Flashes an image to the partition using flash_image for mtd nand
 
 private:
 	bool Can_Be_Mounted;                                                      // Indicates that the partition can be mounted
@@ -208,7 +211,7 @@
 	TWPartition* Find_Partition_By_Path(string Path);                         // Returns a pointer to a partition based on path
 	int Check_Backup_Name(bool Display_Error);                                // Checks the current backup name to ensure that it is valid
 	int Run_Backup();                                                         // Initiates a backup in the current storage
-	int Run_Restore(string Restore_Name);                                     // Restores a backup
+	int Run_Restore(const string& Restore_Name);                              // Restores a backup
 	void Set_Restore_Files(string Restore_Name);                              // Used to gather a list of available backup partitions for the user to select for a restore
 	int Wipe_By_Path(string Path);                                            // Wipes a partition based on path
 	int Wipe_By_Path(string Path, string New_File_System);                    // Wipes a partition based on path
@@ -229,6 +232,7 @@
 	void UnMount_Main_Partitions(void);                                       // Unmounts system and data if not data/media and boot if boot is mountable
 	int Partition_SDCard(void);                                               // Repartitions the sdcard
 	TWPartition *Get_Default_Storage_Partition();                             // Returns a pointer to a default storage partition
+	int Check_Backup_Cancel();                                                // Returns the value of stop_backup
 	int Cancel_Backup();                                                      // Signals partition backup to cancel
 	void Clean_Backup_Folder(string Backup_Folder);                           // Clean Backup Folder on Error
 	int Fix_Contexts();
@@ -255,9 +259,9 @@
 	void Setup_Settings_Storage_Partition(TWPartition* Part);                 // Sets up settings storage
 	void Setup_Android_Secure_Location(TWPartition* Part);                    // Sets up .android_secure if needed
 	bool Make_MD5(bool generate_md5, string Backup_Folder, string Backup_Filename); // Generates an MD5 after a backup is made
-	bool Backup_Partition(TWPartition* Part, string Backup_Folder, bool generate_md5, unsigned long long* img_bytes_remaining, unsigned long long* file_bytes_remaining, unsigned long *img_time, unsigned long *file_time, unsigned long long *img_bytes, unsigned long long *file_bytes);
-	bool Restore_Partition(TWPartition* Part, string Restore_Name, int partition_count, const unsigned long long *total_restore_size, unsigned long long *already_restored_size);
-	void Output_Partition(TWPartition* Part);
+	bool Backup_Partition(TWPartition* Part, const string& Backup_Folder, bool generate_md5, unsigned long *img_time, unsigned long *file_time, ProgressTracking *progress);
+	bool Restore_Partition(TWPartition* Part, const string& Restore_Name, ProgressTracking *progress);
+	void Output_Partition(TWPartition* Part);                                 // Outputs partition details to the log
 	TWPartition* Find_Partition_By_MTP_Storage_ID(unsigned int Storage_ID);   // Returns a pointer to a partition based on MTP Storage ID
 	bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type);         // Adds or removes an MTP Storage partition
 	TWPartition* Find_Next_Storage(string Path, bool Exclude_Data_Media);
diff --git a/progresstracking.cpp b/progresstracking.cpp
new file mode 100644
index 0000000..b43e130
--- /dev/null
+++ b/progresstracking.cpp
@@ -0,0 +1,108 @@
+/*
+        Copyright 2016 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Progress tracking class for tracking backup progess and updating the progress bar as appropriate
+
+
+#include "progresstracking.hpp"
+#include "twcommon.h"
+#ifndef BUILD_TWRPTAR_MAIN
+#include "gui/gui.hpp"
+#include "data.hpp"
+#endif
+#include "twrp-functions.hpp"
+#include <time.h>
+
+const int32_t update_interval_ms = 200; // Update interval in ms
+
+ProgressTracking::ProgressTracking(const unsigned long long backup_size) {
+	total_backup_size = backup_size;
+	partition_size = 0;
+	file_count = 0;
+	current_size = 0;
+	current_count = 0;
+	previous_partitions_size = 0;
+	display_file_count = false;
+	clock_gettime(CLOCK_MONOTONIC, &last_update);
+}
+
+void ProgressTracking::SetPartitionSize(const unsigned long long part_size) {
+	previous_partitions_size += partition_size;
+	partition_size = part_size;
+	UpdateDisplayDetails(true);
+}
+
+void ProgressTracking::SetSizeCount(const unsigned long long part_size, unsigned long long f_count) {
+	previous_partitions_size += partition_size;
+	partition_size = part_size;
+	file_count = f_count;
+	display_file_count = (file_count != 0);
+	UpdateDisplayDetails(true);
+}
+
+void ProgressTracking::UpdateSize(const unsigned long long size) {
+	current_size = size;
+	UpdateDisplayDetails(false);
+}
+
+void ProgressTracking::UpdateSizeCount(const unsigned long long size, const unsigned long long count) {
+	current_size = size;
+	current_count = count;
+	UpdateDisplayDetails(false);
+}
+
+void ProgressTracking::DisplayFileCount(const bool display) {
+	display_file_count = display;
+	UpdateDisplayDetails(true);
+}
+
+void ProgressTracking::UpdateDisplayDetails(const bool force) {
+#ifndef BUILD_TWRPTAR_MAIN
+	if (!force) {
+		// Do something to check the time frame and only update periodically to reduce the total number of GUI updates
+		timespec now;
+		clock_gettime(CLOCK_MONOTONIC, &now);
+
+		int32_t diff = TWFunc::timespec_diff_ms(last_update, now);
+		if (diff < update_interval_ms)
+			return;
+	}
+	clock_gettime(CLOCK_MONOTONIC, &last_update);
+	double display_percent, progress_percent;
+	string size_prog = gui_lookup("size_progress", "%lluMB of %lluMB, %i%%");
+	char size_progress[1024];
+
+	if (total_backup_size != 0) // prevent division by 0
+		display_percent = (double)(current_size + previous_partitions_size) / (double)(total_backup_size) * 100;
+	sprintf(size_progress, size_prog.c_str(), (current_size + previous_partitions_size) / 1048576, total_backup_size / 1048576, (int)(display_percent));
+	DataManager::SetValue("tw_size_progress", size_progress);
+	progress_percent = (display_percent / 100);
+	DataManager::SetProgress((float)(progress_percent));
+
+	if (!display_file_count || file_count == 0) {
+		DataManager::SetValue("tw_file_progress", "");
+	} else {
+		string file_prog = gui_lookup("file_progress", "%llu of %llu files, %i%%");
+		char file_progress[1024];
+
+		display_percent = (double)(current_count) / (double)(file_count) * 100;
+		sprintf(file_progress, file_prog.c_str(), current_count, file_count, (int)(display_percent));
+		DataManager::SetValue("tw_file_progress", file_progress);
+	}
+#endif
+}
diff --git a/progresstracking.hpp b/progresstracking.hpp
new file mode 100644
index 0000000..15cf91c
--- /dev/null
+++ b/progresstracking.hpp
@@ -0,0 +1,54 @@
+/*
+        Copyright 2016 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PROGRESSTRACKING_HPP
+#define __PROGRESSTRACKING_HPP
+
+#include <time.h>
+
+// Progress tracking class for tracking backup progess and updating the progress bar as appropriate
+class ProgressTracking
+{
+public:
+	ProgressTracking(const unsigned long long backup_size);
+
+	void SetPartitionSize(const unsigned long long part_size);
+	void SetSizeCount(const unsigned long long part_size, unsigned long long f_count);
+
+	void UpdateSize(const unsigned long long size);
+	void UpdateSizeCount(const unsigned long long size, const unsigned long long count);
+
+	void DisplayFileCount(const bool display);
+	void UpdateDisplayDetails(const bool force);
+
+private:
+	unsigned long long total_backup_size;              // Overall size (for the progress bar)
+
+	unsigned long long partition_size;                 // Size of the current partition
+	unsigned long long file_count;                     // Count of files for the current partition (tar backup only, not restore)
+
+	unsigned long long current_size;                   // Size of the current partition's already backed up data
+	unsigned long long current_count;                  // Count of files that have already been backed up for the current partition
+
+	unsigned long long previous_partitions_size;       // Total data already backed up from previous partitions (for the progress bar)
+
+	bool display_file_count;                           // Inidicates if we will display the file count text
+	timespec last_update;                              // Tracks last update of the displayed progress (frequent updates tax the CPU and slow us down)
+};
+
+#endif //__PROGRESSTRACKING_HPP
diff --git a/tarWrite.c b/tarWrite.c
index 98fa14c..9846145 100644
--- a/tarWrite.c
+++ b/tarWrite.c
@@ -28,6 +28,8 @@
 unsigned buffer_size = 4096;
 unsigned buffer_loc = 0;
 int buffer_status = 0;
+int prog_pipe = -1;
+const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE);
 
 void reinit_libtar_buffer(void) {
 	flush = 0;
@@ -36,18 +38,20 @@
 	buffer_status = 1;
 }
 
-void init_libtar_buffer(unsigned new_buff_size) {
+void init_libtar_buffer(unsigned new_buff_size, int pipe_fd) {
 	if (new_buff_size != 0)
 		buffer_size = new_buff_size;
 
 	reinit_libtar_buffer();
 	write_buffer = (unsigned char*) malloc(sizeof(char *) * buffer_size);
+	prog_pipe = pipe_fd;
 }
 
 void free_libtar_buffer(void) {
 	if (buffer_status > 0)
 		free(write_buffer);
 	buffer_status = 0;
+	prog_pipe = -1;
 }
 
 ssize_t write_libtar_buffer(int fd, const void *buffer, size_t size) {
@@ -79,6 +83,8 @@
 			buffer_loc = 0;
 			return -1;
 		} else {
+			unsigned long long fs = (unsigned long long)(buffer_loc);
+			write(prog_pipe, &fs, sizeof(fs));
 			buffer_loc = 0;
 			return size;
 		}
@@ -94,3 +100,14 @@
 	if (buffer_status)
 		buffer_status = 2;
 }
+
+void init_libtar_no_buffer(int pipe_fd) {
+	buffer_size = T_BLOCKSIZE;
+	prog_pipe = pipe_fd;
+	buffer_status = 0;
+}
+
+ssize_t write_libtar_no_buffer(int fd, const void *buffer, size_t size) {
+	write(prog_pipe, &progress_size, sizeof(progress_size));
+	return write(fd, buffer, size);
+}
diff --git a/tarWrite.h b/tarWrite.h
index 498ca0a..a63a0f0 100644
--- a/tarWrite.h
+++ b/tarWrite.h
@@ -20,9 +20,12 @@
 #define _TARWRITE_HEADER
 
 void reinit_libtar_buffer();
-void init_libtar_buffer(unsigned new_buff_size);
+void init_libtar_buffer(unsigned new_buff_size, int pipe_fd);
 void free_libtar_buffer();
 writefunc_t write_libtar_buffer(int fd, const void *buffer, size_t size);
 void flush_libtar_buffer(int fd);
 
-#endif  // _TARWRITE_HEADER
\ No newline at end of file
+void init_libtar_no_buffer(int pipe_fd);
+writefunc_t write_libtar_no_buffer(int fd, const void *buffer, size_t size);
+
+#endif  // _TARWRITE_HEADER
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index eb1f4c2..8d85ccf 100644
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -38,6 +38,7 @@
 #include <algorithm>
 #include "twrp-functions.hpp"
 #include "twcommon.h"
+#include "gui/gui.hpp"
 #ifndef BUILD_TWRPTAR_MAIN
 #include "data.hpp"
 #include "partitions.hpp"
@@ -45,7 +46,6 @@
 #include "bootloader.h"
 #include "cutils/properties.h"
 #include "cutils/android_reboot.h"
-#include "gui/gui.hpp"
 #include <sys/reboot.h>
 #endif // ndef BUILD_TWRPTAR_MAIN
 #ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
@@ -272,7 +272,7 @@
 #endif
 }
 
-unsigned long TWFunc::Get_File_Size(string Path) {
+unsigned long TWFunc::Get_File_Size(const string& Path) {
 	struct stat st;
 
 	if (stat(Path.c_str(), &st) != 0)
@@ -329,6 +329,25 @@
 	return res;
 }
 
+timespec TWFunc::timespec_diff(timespec& start, timespec& end)
+{
+	timespec temp;
+	if ((end.tv_nsec-start.tv_nsec)<0) {
+		temp.tv_sec = end.tv_sec-start.tv_sec-1;
+		temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
+	} else {
+		temp.tv_sec = end.tv_sec-start.tv_sec;
+		temp.tv_nsec = end.tv_nsec-start.tv_nsec;
+	}
+	return temp;
+}
+
+int32_t TWFunc::timespec_diff_ms(timespec& start, timespec& end)
+{
+	return ((end.tv_sec * 1000) + end.tv_nsec/1000000) -
+			((start.tv_sec * 1000) + start.tv_nsec/1000000);
+}
+
 #ifndef BUILD_TWRPTAR_MAIN
 
 // Returns "/path" from a full /path/to/file.name
@@ -703,25 +722,6 @@
 	return -1;
 }
 
-timespec TWFunc::timespec_diff(timespec& start, timespec& end)
-{
-	timespec temp;
-	if ((end.tv_nsec-start.tv_nsec)<0) {
-		temp.tv_sec = end.tv_sec-start.tv_sec-1;
-		temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
-	} else {
-		temp.tv_sec = end.tv_sec-start.tv_sec;
-		temp.tv_nsec = end.tv_nsec-start.tv_nsec;
-	}
-	return temp;
-}
-
-int32_t TWFunc::timespec_diff_ms(timespec& start, timespec& end)
-{
-	return ((end.tv_sec * 1000) + end.tv_nsec/1000000) -
-			((start.tv_sec * 1000) + start.tv_nsec/1000000);
-}
-
 bool TWFunc::Install_SuperSU(void) {
 	if (!PartitionManager.Mount_By_Path("/system", true))
 		return false;
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
index d72c916..6c33c5e 100644
--- a/twrp-functions.hpp
+++ b/twrp-functions.hpp
@@ -48,9 +48,11 @@
 	static bool Path_Exists(string Path);                                       // Returns true if the path exists
 	static int Get_File_Type(string fn); // Determines file type, 0 for unknown, 1 for gzip, 2 for OAES encrypted
 	static int Try_Decrypting_File(string fn, string password); // -1 for some error, 0 for failed to decrypt, 1 for decrypted, 3 for decrypted and found gzip format
-	static unsigned long Get_File_Size(string Path);                            // Returns the size of a file
+	static unsigned long Get_File_Size(const string& Path);                            // Returns the size of a file
 	static std::string Remove_Trailing_Slashes(const std::string& path, bool leaveLast = false); // Normalizes the path, e.g /data//media/ -> /data/media
 	static vector<string> split_string(const string &in, char del, bool skip_empty);
+	static timespec timespec_diff(timespec& start, timespec& end);	            // Return a diff for 2 times
+	static int32_t timespec_diff_ms(timespec& start, timespec& end);            // Returns diff in ms
 
 #ifndef BUILD_TWRPTAR_MAIN
 	static void install_htc_dumlock(void);                                      // Installs HTC Dumlock
@@ -66,8 +68,6 @@
 	static int removeDir(const string path, bool removeParent); //recursively remove a directory
 	static int copy_file(string src, string dst, int mode); //copy file from src to dst with mode permissions
 	static unsigned int Get_D_Type_From_Stat(string Path);                      // Returns a dirent dt_type value using stat instead of dirent
-	static timespec timespec_diff(timespec& start, timespec& end);	            // Return a diff for 2 times
-	static int32_t timespec_diff_ms(timespec& start, timespec& end);            // Returns diff in ms
 	static int read_file(string fn, vector<string>& results); //read from file
 	static int read_file(string fn, string& results); //read from file
 	static int read_file(string fn, uint64_t& results); //read from file
diff --git a/twrpDigest.cpp b/twrpDigest.cpp
index fe0e067..0dd3ea9 100644
--- a/twrpDigest.cpp
+++ b/twrpDigest.cpp
@@ -1,5 +1,5 @@
 /*
-	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -44,7 +44,7 @@
 
 using namespace std;
 
-void twrpDigest::setfn(string fn) {
+void twrpDigest::setfn(const string& fn) {
 	md5fn = fn;
 }
 
diff --git a/twrpDigest.hpp b/twrpDigest.hpp
index 5edd4d3..2c08ec5 100644
--- a/twrpDigest.hpp
+++ b/twrpDigest.hpp
@@ -1,5 +1,5 @@
 /*
-        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin
         This file is part of TWRP/TeamWin Recovery Project.
 
         TWRP is free software: you can redistribute it and/or modify
@@ -25,7 +25,7 @@
 class twrpDigest
 {
 public:
-	void setfn(string fn);
+	void setfn(const string& fn);
 	int computeMD5(void);
 	int verify_md5digest(void);
 	int write_md5digest(void);
diff --git a/twrpTar.cpp b/twrpTar.cpp
index 76c8920..b46f10f 100644
--- a/twrpTar.cpp
+++ b/twrpTar.cpp
@@ -1,6 +1,6 @@
 
 /*
-	Copyright 2013 TeamWin
+	Copyright 2013 to 2016 bigbiff/Dees_Troy TeamWin
 	This file is part of TWRP/TeamWin Recovery Project.
 
 	TWRP is free software: you can redistribute it and/or modify
@@ -42,10 +42,11 @@
 #include "twcommon.h"
 #include "variables.h"
 #include "twrp-functions.hpp"
+#include "gui/gui.hpp"
+#include "progresstracking.hpp"
 #ifndef BUILD_TWRPTAR_MAIN
 #include "data.hpp"
 #include "infomanager.hpp"
-#include "gui/gui.hpp"
 extern "C" {
 	#include "set_metadata.h"
 }
@@ -90,7 +91,7 @@
 	_exit(255);
 }
 
-int twrpTar::createTarFork(const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &fork_pid) {
+int twrpTar::createTarFork(ProgressTracking *progress, pid_t &fork_pid) {
 	int status = 0;
 	pid_t rc_pid, tar_fork_pid;
 	int progress_pipe[2], ret;
@@ -389,15 +390,8 @@
 		}
 	} else {
 		// Parent side
-		unsigned long long fs, size_backup, files_backup, total_backup_size;
+		unsigned long long fs, size_backup = 0, files_backup = 0, file_count = 0;
 		int first_data = 0;
-		double display_percent, progress_percent;
-		char file_progress[1024];
-		char size_progress[1024];
-		files_backup = 0;
-		size_backup = 0;
-		string file_prog = gui_lookup("file_progress", "%llu of %llu files, %i%%");
-		string size_prog = gui_lookup("size_progress", "%lluMB of %lluMB, %i%%");
 
 		fork_pid = tar_fork_pid;
 
@@ -413,27 +407,24 @@
 				first_data = 1;
 			} else if (first_data == 1) {
 				// Second incoming data is total size
-				total_backup_size = fs;
 				first_data = 2;
+				progress->SetSizeCount(fs, file_count);
 			} else {
-				files_backup++;
-				size_backup += fs;
-				display_percent = (double)(files_backup) / (double)(file_count) * 100;
-				sprintf(file_progress, file_prog.c_str(), files_backup, file_count, (int)(display_percent));
-#ifndef BUILD_TWRPTAR_MAIN
-				DataManager::SetValue("tw_file_progress", file_progress);
-				display_percent = (double)(size_backup + *other_backups_size) / (double)(*overall_size) * 100;
-				sprintf(size_progress, size_prog.c_str(), (size_backup + *other_backups_size) / 1048576, *overall_size / 1048576, (int)(display_percent));
-				DataManager::SetValue("tw_size_progress", size_progress);
-				progress_percent = (display_percent / 100);
-				DataManager::SetProgress((float)(progress_percent));
-#endif //ndef BUILD_TWRPTAR_MAIN
+				if (fs > 0) {
+					size_backup += fs;
+					progress->UpdateSize(size_backup);
+				} else { // fs == 0 increments the file counter
+					files_backup++;
+					progress->UpdateSizeCount(size_backup, files_backup);
+				}
 			}
 		}
 		close(progress_pipe[0]);
 #ifndef BUILD_TWRPTAR_MAIN
 		DataManager::SetValue("tw_file_progress", "");
 		DataManager::SetValue("tw_size_progress", "");
+		progress->DisplayFileCount(false);
+		progress->UpdateDisplayDetails(true);
 
 		InfoManager backup_info(backup_folder + partition_name + ".info");
 		backup_info.SetValue("backup_size", size_backup);
@@ -454,7 +445,7 @@
 	return 0;
 }
 
-int twrpTar::extractTarFork(const unsigned long long *overall_size, unsigned long long *other_backups_size) {
+int twrpTar::extractTarFork(ProgressTracking *progress) {
 	int status = 0;
 	pid_t rc_pid, tar_fork_pid;
 	int progress_pipe[2], ret;
@@ -597,11 +588,7 @@
 		}
 		else // parent process
 		{
-			unsigned long long fs, size_backup;
-			double display_percent, progress_percent;
-			char size_progress[1024];
-			size_backup = 0;
-			string size_prog = gui_lookup("size_progress", "%lluMB of %lluMB, %i%%");
+			unsigned long long fs, size_backup = 0;
 
 			// Parent closes output side
 			close(progress_pipe[1]);
@@ -609,19 +596,10 @@
 			// Read progress data from children
 			while (read(progress_pipe[0], &fs, sizeof(fs)) > 0) {
 				size_backup += fs;
-				display_percent = (double)(size_backup + *other_backups_size) / (double)(*overall_size) * 100;
-				sprintf(size_progress, size_prog.c_str(), (size_backup + *other_backups_size) / 1048576, *overall_size / 1048576, (int)(display_percent));
-				progress_percent = (display_percent / 100);
-#ifndef BUILD_TWRPTAR_MAIN
-				DataManager::SetValue("tw_size_progress", size_progress);
-				DataManager::SetProgress((float)(progress_percent));
-#endif //ndef BUILD_TWRPTAR_MAIN
+				progress->UpdateSize(size_backup);
 			}
 			close(progress_pipe[0]);
-#ifndef BUILD_TWRPTAR_MAIN
-			DataManager::SetValue("tw_file_progress", "");
-#endif //ndef BUILD_TWRPTAR_MAIN
-			*other_backups_size += size_backup;
+			progress->UpdateDisplayDetails(true);
 
 			if (TWFunc::Wait_For_Child(tar_fork_pid, &status, "extractTarFork()") != 0)
 				return -1;
@@ -786,6 +764,7 @@
 					Archive_Current_Size = 0;
 				}
 				Archive_Current_Size += fs;
+				fs = 0; // Sending a 0 size to the pipe tells it to increment the file counter
 				write(progress_pipe_fd, &fs, sizeof(fs));
 			}
 			LOGINFO("addFile '%s' including root: %i\n", buf, include_root_dir);
@@ -862,7 +841,6 @@
 int twrpTar::createTar() {
 	char* charTarFile = (char*) tarfn.c_str();
 	char* charRootDir = (char*) tardir.c_str();
-	static tartype_t type = { open, close, read, write_tar };
 
 	if (use_encryption && use_compression) {
 		// Compressed and encrypted
@@ -945,7 +923,9 @@
 				close(pipes[2]);
 				close(pipes[3]);
 				fd = pipes[1];
-				if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+				init_libtar_no_buffer(progress_pipe_fd);
+				tar_type = { open, close, read, write_tar_no_buffer };
+				if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
 					close(fd);
 					LOGINFO("tar_fdopen failed\n");
 					gui_err("backup_error=Error creating backup.");
@@ -997,7 +977,9 @@
 			// Parent
 			close(pigzfd[0]); // close parent input
 			fd = pigzfd[1];   // copy parent output
-			if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+			init_libtar_no_buffer(progress_pipe_fd);
+			tar_type = { open, close, read, write_tar_no_buffer };
+			if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
 				close(fd);
 				LOGINFO("tar_fdopen failed\n");
 				gui_err("backup_error=Error creating backup.");
@@ -1045,7 +1027,9 @@
 			// Parent
 			close(oaesfd[0]); // close parent input
 			fd = oaesfd[1];   // copy parent output
-			if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+			init_libtar_no_buffer(progress_pipe_fd);
+			tar_type = { open, close, read, write_tar_no_buffer };
+			if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
 				close(fd);
 				LOGINFO("tar_fdopen failed\n");
 				gui_err("backup_error=Error creating backup.");
@@ -1055,8 +1039,9 @@
 		}
 	} else {
 		// Not compressed or encrypted
-		init_libtar_buffer(0);
-		if (tar_open(&t, charTarFile, &type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) {
+		init_libtar_buffer(0, progress_pipe_fd);
+		tar_type = { open, close, read, write_tar };
+		if (tar_open(&t, charTarFile, &tar_type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) {
 			LOGINFO("tar_open error opening '%s'\n", tarfn.c_str());
 			gui_err("backup_error=Error creating backup.");
 			return -1;
@@ -1476,3 +1461,7 @@
 extern "C" ssize_t write_tar(int fd, const void *buffer, size_t size) {
 	return (ssize_t) write_libtar_buffer(fd, buffer, size);
 }
+
+extern "C" ssize_t write_tar_no_buffer(int fd, const void *buffer, size_t size) {
+	return (ssize_t) write_libtar_no_buffer(fd, buffer, size);
+}
diff --git a/twrpTar.h b/twrpTar.h
index a73b917..425a831 100644
--- a/twrpTar.h
+++ b/twrpTar.h
@@ -20,6 +20,7 @@
 #define _TWRPTAR_HEADER
 
 ssize_t write_tar(int fd, const void *buffer, size_t size);
+ssize_t write_tar_no_buffer(int fd, const void *buffer, size_t size);
 
 #endif  // _TWRPTAR_HEADER
 
diff --git a/twrpTar.hpp b/twrpTar.hpp
index a486c41..8ef5020 100644
--- a/twrpTar.hpp
+++ b/twrpTar.hpp
@@ -1,5 +1,5 @@
 /*
-        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin
         This file is part of TWRP/TeamWin Recovery Project.
 
         TWRP is free software: you can redistribute it and/or modify
@@ -28,6 +28,7 @@
 #include <string>
 #include <vector>
 #include "twrpDU.hpp"
+#include "progresstracking.hpp"
 
 using namespace std;
 
@@ -45,8 +46,8 @@
 public:
 	twrpTar();
 	virtual ~twrpTar();
-	int createTarFork(const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &fork_pid);
-	int extractTarFork(const unsigned long long *overall_size, unsigned long long *other_backups_size);
+	int createTarFork(ProgressTracking *progress, pid_t &fork_pid);
+	int extractTarFork(ProgressTracking *progress);
 	void setfn(string fn);
 	void setdir(string dir);
 	void setsize(unsigned long long backup_size);
@@ -87,6 +88,7 @@
 	unsigned long long Total_Backup_Size;
 	bool include_root_dir;
 	TAR *t;
+	tartype_t tar_type; // Only used in createTar() but variable must persist while the tar is open
 	int fd;
 	pid_t pigz_pid;
 	pid_t oaes_pid;
diff --git a/twrpTarMain/Android.mk b/twrpTarMain/Android.mk
index 5a1a443..c88351b 100644
--- a/twrpTarMain/Android.mk
+++ b/twrpTarMain/Android.mk
@@ -8,18 +8,21 @@
 	../twrp-functions.cpp \
 	../twrpTar.cpp \
 	../tarWrite.c \
-	../twrpDU.cpp
+	../twrpDU.cpp \
+	../progresstracking.cpp \
+	../gui/twmsg.cpp
 LOCAL_CFLAGS:= -g -c -W -DBUILD_TWRPTAR_MAIN
 
 LOCAL_C_INCLUDES += bionic
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
-    LOCAL_C_INCLUDES += external/stlport/stlport
+    LOCAL_C_INCLUDES += external/stlport/stlport bionic/libstdc++/include
 endif
 
-LOCAL_STATIC_LIBRARIES := libc libtar_static libstdc++
+LOCAL_STATIC_LIBRARIES := libc libtar_static
 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
     LOCAL_STATIC_LIBRARIES += libstlport_static
 endif
+LOCAL_STATIC_LIBRARIES += libstdc++
 
 ifeq ($(TWHAVE_SELINUX), true)
     LOCAL_C_INCLUDES += external/libselinux/include
@@ -51,7 +54,9 @@
 	../twrp-functions.cpp \
 	../twrpTar.cpp \
 	../tarWrite.c \
-	../twrpDU.cpp
+	../twrpDU.cpp \
+	../progresstracking.cpp \
+	../gui/twmsg.cpp
 LOCAL_CFLAGS:= -g -c -W -DBUILD_TWRPTAR_MAIN
 
 LOCAL_C_INCLUDES += bionic external/stlport/stlport
diff --git a/twrpTarMain/twrpTarMain.cpp b/twrpTarMain/twrpTarMain.cpp
index 6cc629d..ff35f47 100644
--- a/twrpTarMain/twrpTarMain.cpp
+++ b/twrpTarMain/twrpTarMain.cpp
@@ -20,10 +20,52 @@
 #include "../twrp-functions.hpp"
 #include "../twrpTar.hpp"
 #include "../twrpDU.hpp"
+#include "../progresstracking.hpp"
+#include "../gui/gui.hpp"
+#include "../gui/twmsg.h"
 #include <string.h>
 
 twrpDU du;
 
+void gui_msg(const char* text)
+{
+	if (text) {
+		Message msg = Msg(text);
+		gui_msg(msg);
+	}
+}
+
+void gui_warn(const char* text)
+{
+	if (text) {
+		Message msg = Msg(msg::kWarning, text);
+		gui_msg(msg);
+	}
+}
+
+void gui_err(const char* text)
+{
+	if (text) {
+		Message msg = Msg(msg::kError, text);
+		gui_msg(msg);
+	}
+}
+
+void gui_highlight(const char* text)
+{
+	if (text) {
+		Message msg = Msg(msg::kHighlight, text);
+		gui_msg(msg);
+	}
+}
+
+void gui_msg(Message msg)
+{
+	std::string output = msg;
+	output += "\n";
+	fputs(output.c_str(), stdout);
+}
+
 void usage() {
 	printf("twrpTar <action> [options]\n\n");
 	printf("actions: -c create\n");
@@ -34,7 +76,7 @@
 	printf(" -z    compress backup (/sbin/pigz must be present)\n");
 #ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
 	printf(" -e    encrypt/decrypt backup followed by password (/sbin/openaes must be present)\n");
-	printf(" -u    encrypt using userdata encryption (must be used with -e\n");
+	printf(" -u    encrypt using userdata encryption (must be used with -e)\n");
 #endif
 	printf("\n\n");
 	printf("Example: twrpTar -c -d /cache -t /sdcard/test.tar\n");
@@ -47,7 +89,7 @@
 	int i, action = 0;
 	unsigned j;
 	string Directory, Tar_Filename;
-	unsigned long long temp1 = 0, temp2 = 0;
+	ProgressTracking progress(1);
 	pid_t tar_fork_pid = 0;
 #ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
 	string Password;
@@ -144,14 +186,14 @@
 	}
 #endif
 	if (action == 1) {
-		if (tar.createTarFork(&temp1, &temp2, tar_fork_pid) != 0) {
+		if (tar.createTarFork(&progress, tar_fork_pid) != 0) {
 			sync();
 			return -1;
 		}
 		sync();
 		printf("\n\ntar created successfully.\n");
 	} else if (action == 2) {
-		if (tar.extractTarFork(&temp1, &temp2) != 0) {
+		if (tar.extractTarFork(&progress) != 0) {
 			sync();
 			return -1;
 		}