Use magiskboot to repack the boot partition

Set TW_INCLUDE_REPACKTOOLS := true

Must also have:
AB_OTA_UPDATER := true

Use magiskboot and provide GUI options to allow users to repack
their existing boot image to install TWRP (or kernels) so we can
stop having to provide installation zips for AB devices. There is
also an option to try to fix a recovery bootloop if the kernel
has been patched to always boot the ramdisk for root, etc.

You will need to pull the below repo into external/magisk-prebuilt
https://github.com/TeamWin/external_magisk-prebuilt

Change-Id: I74196cc6f095a7576d61886dc96cbc18deba9b04
diff --git a/data.cpp b/data.cpp
index 9225535..cd03d78 100755
--- a/data.cpp
+++ b/data.cpp
@@ -916,7 +916,12 @@
 	mData.SetValue("tw_app_install_status", "0"); // 0 = no status, 1 = not installed, 2 = already installed
 #endif
 
-        mData.SetValue("tw_enable_adb_backup", "0");
+	mData.SetValue("tw_enable_adb_backup", "0");
+
+	if (TWFunc::Path_Exists("/sbin/magiskboot"))
+		mConst.SetValue("tw_has_repack_tools", "1");
+	else
+		mConst.SetValue("tw_has_repack_tools", "0");
 
 	pthread_mutex_unlock(&m_valuesLock);
 }
diff --git a/gui/action.cpp b/gui/action.cpp
index 4b644a9..c4e78cf 100755
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -231,6 +231,8 @@
 		ADD_ACTION(twcmd);
 		ADD_ACTION(setbootslot);
 		ADD_ACTION(installapp);
+		ADD_ACTION(repackimage);
+		ADD_ACTION(fixabrecoverybootloop);
 	}
 
 	// First, get the action
@@ -1196,7 +1198,7 @@
 			string Backup_Name;
 			DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
 			string auto_gen = gui_lookup("auto_generate", "(Auto Generate)");
-			if (Backup_Name == auto_gen || Backup_Name == gui_lookup("curr_date", "(Current Date)") || Backup_Name == "0" || Backup_Name == "(" || PartitionManager.Check_Backup_Name(true) == 0) {
+			if (Backup_Name == auto_gen || Backup_Name == gui_lookup("curr_date", "(Current Date)") || Backup_Name == "0" || Backup_Name == "(" || PartitionManager.Check_Backup_Name(Backup_Name, true, true) == 0) {
 				ret = PartitionManager.Run_Backup(false);
 				DataManager::SetValue("tw_encrypt_backup", 0); // reset value so we don't encrypt every subsequent backup
 				if (!PartitionManager.stop_backup.get_value()) {
@@ -1472,7 +1474,9 @@
 	if (simulate) {
 		simulate_progress_bar();
 	} else {
-		op_status = PartitionManager.Check_Backup_Name(true);
+		string Backup_Name;
+		DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+		op_status = PartitionManager.Check_Backup_Name(Backup_Name, true, true);
 		if (op_status != 0)
 			op_status = 1;
 	}
@@ -2053,3 +2057,89 @@
 	operation_end(0);
 	return 0;
 }
+
+int GUIAction::repackimage(std::string arg __unused)
+{
+	int op_status = 1;
+	operation_start("Repack Image");
+	if (!simulate)
+	{
+		std::string path = DataManager::GetStrValue("tw_filename");
+		Repack_Options_struct Repack_Options;
+		Repack_Options.Disable_Verity = false;
+		Repack_Options.Disable_Force_Encrypt = false;
+		Repack_Options.Backup_First = DataManager::GetIntValue("tw_repack_backup_first") != 0;
+		if (DataManager::GetIntValue("tw_repack_kernel") == 1)
+			Repack_Options.Type = REPLACE_KERNEL;
+		else
+			Repack_Options.Type = REPLACE_RAMDISK;
+		if (!PartitionManager.Repack_Images(path, Repack_Options))
+			goto exit;
+	} else
+		simulate_progress_bar();
+	op_status = 0;
+exit:
+	operation_end(op_status);
+	return 0;
+}
+
+int GUIAction::fixabrecoverybootloop(std::string arg __unused)
+{
+	int op_status = 1;
+	operation_start("Repack Image");
+	if (!simulate)
+	{
+		if (!TWFunc::Path_Exists("/sbin/magiskboot")) {
+			LOGERR("Image repacking tool not present in this TWRP build!");
+			goto exit;
+		}
+		DataManager::SetProgress(0);
+		TWPartition* part = PartitionManager.Find_Partition_By_Path("/boot");
+		if (part)
+			gui_msg(Msg("unpacking_image=Unpacking {1}...")(part->Display_Name));
+		else {
+			gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/boot"));
+			goto exit;
+		}
+		if (!PartitionManager.Prepare_Repack(part, REPACK_ORIG_DIR, DataManager::GetIntValue("tw_repack_backup_first") != 0, gui_lookup("repack", "Repack")))
+			goto exit;
+		DataManager::SetProgress(.25);
+		gui_msg("fixing_recovery_loop_patch=Patching kernel...");
+		std::string command = "cd " REPACK_ORIG_DIR " && /sbin/magiskboot --hexpatch kernel 77616E745F696E697472616D667300 736B69705F696E697472616D667300";
+		if (TWFunc::Exec_Cmd(command) != 0) {
+			gui_msg(Msg(msg::kError, "fix_recovery_loop_patch_error=Error patching kernel."));
+			goto exit;
+		}
+		std::string header_path = REPACK_ORIG_DIR;
+		header_path += "header";
+		if (TWFunc::Path_Exists(header_path)) {
+			command = "cd " REPACK_ORIG_DIR " && sed -i \"s|$(grep '^cmdline=' header | cut -d= -f2-)|$(grep '^cmdline=' header | cut -d= -f2- | sed -e 's/skip_override//' -e 's/  */ /g' -e 's/[ \t]*$//')|\" header";
+			if (TWFunc::Exec_Cmd(command) != 0) {
+				gui_msg(Msg(msg::kError, "fix_recovery_loop_patch_error=Error patching kernel."));
+				goto exit;
+			}
+		}
+		DataManager::SetProgress(.5);
+		gui_msg(Msg("repacking_image=Repacking {1}...")(part->Display_Name));
+		command = "cd " REPACK_ORIG_DIR " && /sbin/magiskboot --repack " REPACK_ORIG_DIR "boot.img";
+		if (TWFunc::Exec_Cmd(command) != 0) {
+			gui_msg(Msg(msg::kError, "repack_error=Error repacking image."));
+			goto exit;
+		}
+		DataManager::SetProgress(.75);
+		std::string path = REPACK_ORIG_DIR;
+		std::string file = "new-boot.img";
+		DataManager::SetValue("tw_flash_partition", "/boot;");
+		if (!PartitionManager.Flash_Image(path, file)) {
+			LOGINFO("Error flashing new image\n");
+			goto exit;
+		}
+		DataManager::SetProgress(1);
+		TWFunc::removeDir(REPACK_ORIG_DIR, false);
+	} else
+		simulate_progress_bar();
+	op_status = 0;
+exit:
+	operation_end(op_status);
+	return 0;
+}
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 630cf71..2e306e0 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -367,6 +367,8 @@
 	int twcmd(std::string arg);
 	int setbootslot(std::string arg);
 	int installapp(std::string arg);
+	int repackimage(std::string arg);
+	int fixabrecoverybootloop(std::string arg);
 
 	int simulate;
 };
diff --git a/gui/theme/common/landscape.xml b/gui/theme/common/landscape.xml
index a3e6ed8..bb9878f 100755
--- a/gui/theme/common/landscape.xml
+++ b/gui/theme/common/landscape.xml
@@ -3398,6 +3398,41 @@
 						<action function="page">confirm_action</action>
 					</actions>
 				</listitem>
+				<listitem name="{@install_twrp_ramdisk=Install Recovery Ramdisk}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=0</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@install_kernel=Install Kernel}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=1</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@fix_recovery_loop=Fix Recovery Bootloop}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_back=advanced</action>
+						<action function="set">tw_action=fixabrecoverybootloop</action>
+						<action function="set">tw_text1={@fix_recovery_loop_confirm=Fix Recovery Bootloop?}</action>
+						<action function="set">tw_action_text1={@fixing_recovery_loop=Fixing Recovery Bootloop...}</action>
+						<action function="set">tw_complete_text1={@fix_recovery_loop_complete=Fix Recovery Bootloop Complete}</action>
+						<action function="set">tw_slider_text={@swipe_to_confirm=Swipe to Confirm}</action>
+						<action function="page">confirm_action</action>
+					</actions>
+				</listitem>
 			</listbox>
 
 			<action>
@@ -3657,6 +3692,140 @@
 			</action>
 		</page>
 
+		<page name="repackselect">
+			<template name="page"/>
+
+			<text style="text_l">
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_image_hdr=Select Image}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@select_file_from_storage=Select File from %tw_storage_display_name% (%tw_storage_free_size% MB)}</text>
+			</text>
+
+			<template name="sort_options"/>
+
+			<fileselector>
+				<placement x="%col1_x_left%" y="%row1a_y%" w="%content_quarter_width%" h="%fileselector_filemanager_height%"/>
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" folders="1" files="1"/>
+				<path name="tw_zip_location" default="/sdcard"/>
+				<data name="tw_filename"/>
+				<selection name="tw_file"/>
+			</fileselector>
+
+			<button style="main_button_half_width_low">
+				<placement x="%col_button_right%" y="%row16a_y%"/>
+				<text>{@select_storage_btn=Select Storage}</text>
+				<actions>
+					<action function="set">tw_back=install</action>
+					<action function="overlay">select_storage</action>
+				</actions>
+			</button>
+
+			<action>
+				<conditions>
+					<condition var1="tw_filename" op="modified"/>
+				</conditions>
+				<action function="page">repackconfirm</action>
+			</action>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">advanced</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
+		<page name="repackconfirm">
+			<template name="page"/>
+
+			<text style="text_l">
+				<condition var1="tw_repack_kernel" var2="1"/>
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_kernel_confirm_hdr=Install Kernel}</text>
+			</text>
+
+			<text style="text_l">
+				<condition var1="tw_repack_kernel" var2="0"/>
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_ramdisk_confirm_hdr=Install Recovery}</text>
+			</text>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="1"/>
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@repack_kernel_confirm=Install Kernel?}</text>
+			</text>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="0"/>
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@repack_ramdisk_confirm=Install Recovery?}</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row2_y%"/>
+				<text>{@folder=Folder:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row3_y%"/>
+				<text>%tw_zip_location%</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row4_y%"/>
+				<text>{@file=File:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row5_y%"/>
+				<text>%tw_file%</text>
+			</text>
+
+			<checkbox>
+				<placement x="%indent%" y="%row7_y%"/>
+				<text>{@repack_backup_first=Back up existing image first}</text>
+				<data variable="tw_repack_backup_first"/>
+			</checkbox>
+
+			<button style="main_button_half_width">
+				<placement x="%col1_x_left%" y="%row15a_y%"/>
+				<text>{@install_cancel=Do not Install}</text>
+				<action function="page">repackselect</action>
+			</button>
+
+			<slider style="slider_centered">
+				<text>{@swipe_to_install=Swipe to Install}</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=repackimage</action>
+					<action function="set">tw_action_param=/boot</action>
+					<action function="set">tw_action_text1={@installing=Installing...}</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1={@install_complete=Install Complete}</action>
+					<action function="page">action_page</action>
+				</actions>
+			</slider>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">repackselect</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
 		<page name="lock">
 			<background color="%semi_transparent%"/>
 
diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml
index e61f6c0..7a60120 100755
--- a/gui/theme/common/languages/en.xml
+++ b/gui/theme/common/languages/en.xml
@@ -486,6 +486,28 @@
 		<string name="install_cancel">Do Not Install</string>
 		<string name="sel_storage_list">Select Storage</string>
 		<string name="ok_btn">OK</string>
+		<string name="install_twrp_ramdisk">Install Recovery Ramdisk</string>
+		<string name="install_kernel">Install Kernel</string>
+		<string name="repack_kernel_confirm_hdr">Install Kernel</string>
+		<string name="repack_ramdisk_confirm_hdr">Install Recovery</string>
+		<string name="repack_kernel_confirm">Install Kernel?</string>
+		<string name="repack_ramdisk_confirm">Install Recovery?</string>
+		<string name="repack_backup_first">Back up existing image first</string>
+		<string name="repack">Repack</string>
+		<string name="swipe_to_install">Swipe to Install</string>
+		<string name="installing">Installing...</string>
+		<string name="install_complete">Install Complete</string>
+		<string name="unpack_error">Error unpacking image.</string>
+		<string name="repack_error">Error repacking image.</string>
+		<string name="unpacking_image">Unpacking {1}...</string>
+		<string name="repacking_image">Repacking {1}...</string>
+		<string name="repack_image_hdr">Select Image</string>
+		<string name="fix_recovery_loop">Fix Recovery Bootloop</string>
+		<string name="fix_recovery_loop_confirm">Fix Recovery Bootloop?</string>
+		<string name="fixing_recovery_loop">Fixing Recovery Bootloop...</string>
+		<string name="fix_recovery_loop_complete">Fix Recovery Bootloop Complete</string>
+		<string name="fixing_recovery_loop_patch">Patching kernel...</string>
+		<string name="fix_recovery_loop_patch_error">Error patching kernel.</string>
 
 		<!-- Various console messages - these consist of user displayed messages, oftentimes errors -->
 		<string name="no_kernel_selinux">Kernel does not have support for reading SELinux contexts.</string>
diff --git a/gui/theme/common/portrait.xml b/gui/theme/common/portrait.xml
index c993225..691f149 100755
--- a/gui/theme/common/portrait.xml
+++ b/gui/theme/common/portrait.xml
@@ -3559,6 +3559,41 @@
 						<action function="page">confirm_action</action>
 					</actions>
 				</listitem>
+				<listitem name="{@install_twrp_ramdisk=Install Recovery Ramdisk}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=0</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@install_kernel=Install Kernel}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=1</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@fix_recovery_loop=Fix Recovery Bootloop}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_back=advanced</action>
+						<action function="set">tw_action=fixabrecoverybootloop</action>
+						<action function="set">tw_text1={@fix_recovery_loop_confirm=Fix Recovery Bootloop?}</action>
+						<action function="set">tw_action_text1={@fixing_recovery_loop=Fixing Recovery Bootloop...}</action>
+						<action function="set">tw_complete_text1={@fix_recovery_loop_complete=Fix Recovery Bootloop Complete}</action>
+						<action function="set">tw_slider_text={@swipe_to_confirm=Swipe to Confirm}</action>
+						<action function="page">confirm_action</action>
+					</actions>
+				</listitem>
 			</listbox>
 
 			<action>
@@ -3821,6 +3856,140 @@
 			</action>
 		</page>
 
+		<page name="repackselect">
+			<template name="page"/>
+
+			<text style="text_l">
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_image_hdr=Select Image}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@select_file_from_storage=Select File from %tw_storage_display_name% (%tw_storage_free_size% MB)}</text>
+			</text>
+
+			<template name="sort_options"/>
+
+			<fileselector>
+				<placement x="%indent%" y="%row3_y%" w="%content_width%" h="%fileselector_install_height%"/>
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" folders="1" files="1"/>
+				<path name="tw_zip_location" default="/sdcard"/>
+				<data name="tw_filename"/>
+				<selection name="tw_file"/>
+			</fileselector>
+
+			<button style="main_button_half_height">
+				<placement x="%indent%" y="%row21a_y%"/>
+				<text>{@select_storage_btn=Select Storage}</text>
+				<actions>
+					<action function="set">tw_back=repackselect</action>
+					<action function="overlay">select_storage</action>
+				</actions>
+			</button>
+
+			<action>
+				<conditions>
+					<condition var1="tw_filename" op="modified"/>
+				</conditions>
+				<action function="page">repackconfirm</action>
+			</action>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">advanced</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
+		<page name="repackconfirm">
+			<template name="page"/>
+
+			<text style="text_l">
+				<condition var1="tw_repack_kernel" var2="1"/>
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_kernel_confirm_hdr=Install Kernel}</text>
+			</text>
+
+			<text style="text_l">
+				<condition var1="tw_repack_kernel" var2="0"/>
+				<placement x="%col1_x_header%" y="%row3_header_y%"/>
+				<text>{@repack_ramdisk_confirm_hdr=Install Recovery}</text>
+			</text>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="1"/>
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@repack_kernel_confirm=Install Kernel?}</text>
+			</text>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="0"/>
+				<placement x="%col1_x_header%" y="%row4_header_y%"/>
+				<text>{@repack_ramdisk_confirm=Install Recovery?}</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row2_y%"/>
+				<text>{@folder=Folder:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row3_y%"/>
+				<text>%tw_zip_location%</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row4_y%"/>
+				<text>{@file=File:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row5_y%"/>
+				<text>%tw_file%</text>
+			</text>
+
+			<checkbox>
+				<placement x="%indent%" y="%row7_y%"/>
+				<text>{@repack_backup_first=Back up existing image first}</text>
+				<data variable="tw_repack_backup_first"/>
+			</checkbox>
+
+			<button style="main_button_half_height">
+				<placement x="%indent%" y="%row16_y%"/>
+				<text>{@install_cancel=Do not Install}</text>
+				<action function="page">repackselect</action>
+			</button>
+
+			<slider>
+				<text>{@swipe_to_install=Swipe to Install}</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=repackimage</action>
+					<action function="set">tw_action_param=/boot</action>
+					<action function="set">tw_action_text1={@installing=Installing...}</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1={@install_complete=Install Complete}</action>
+					<action function="page">action_page</action>
+				</actions>
+			</slider>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">repackselect</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
 		<page name="lock">
 			<background color="%semi_transparent%"/>
 
diff --git a/gui/theme/common/watch.xml b/gui/theme/common/watch.xml
index 79ac5ec..fcb00bc 100755
--- a/gui/theme/common/watch.xml
+++ b/gui/theme/common/watch.xml
@@ -4170,6 +4170,41 @@
 						<action function="page">confirm_action</action>
 					</actions>
 				</listitem>
+				<listitem name="{@install_twrp_ramdisk=Install Recovery Ramdisk}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=0</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@install_kernel=Install Kernel}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_repack_kernel=1</action>
+						<action function="page">repackselect</action>
+					</actions>
+				</listitem>
+				<listitem name="{@fix_recovery_loop=Fix Recovery Bootloop}">
+					<conditions>
+						<condition var1="tw_has_boot_slots" var2="1"/>
+						<condition var1="tw_has_repack_tools" var2="1"/>
+					</conditions>
+					<actions>
+						<action function="set">tw_back=advanced</action>
+						<action function="set">tw_action=fixabrecoverybootloop</action>
+						<action function="set">tw_text1={@fix_recovery_loop_confirm=Fix Recovery Bootloop?}</action>
+						<action function="set">tw_action_text1={@fixing_recovery_loop=Fixing Recovery Bootloop...}</action>
+						<action function="set">tw_complete_text1={@fix_recovery_loop_complete=Fix Recovery Bootloop Complete}</action>
+						<action function="set">tw_slider_text={@swipe_to_confirm=Swipe to Confirm}</action>
+						<action function="page">confirm_action</action>
+					</actions>
+				</listitem>
 			</listbox>
 
 			<button>
@@ -4576,6 +4611,121 @@
 			</action>
 		</page>
 
+		<page name="repackselect">
+			<template name="page"/>
+
+			<template name="statusbar"/>
+
+			<text style="text_m">
+				<placement x="%col1_x_left%" y="%row1_header_y%"/>
+				<text>{@repack_image_hdr=Select Image}</text>
+			</text>
+
+			<fileselector>
+				<placement x="%indent%" y="%row2_header_y%" w="%content_width%" h="%fileselector_install_height%"/>
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" folders="1" files="1"/>
+				<path name="tw_zip_location" default="/sdcard"/>
+				<data name="tw_filename"/>
+				<selection name="tw_file"/>
+			</fileselector>
+
+			<button>
+				<placement x="%btn4_col4_x%" y="%row11_y%"/>
+				<highlight color="%highlight_color%"/>
+				<image resource="q_btn_storage"/>
+				<actions>
+					<action function="set">tw_storagetext={@install_hdr=Install} &gt; {@select_storage_hdr=Select Storage}</action>
+					<action function="set">tw_back=install</action>
+					<action function="page">select_storage</action>
+				</actions>
+			</button>
+
+			<action>
+				<conditions>
+					<condition var1="tw_filename" op="modified"/>
+				</conditions>
+				<action function="page">repackconfirm</action>
+			</action>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">advanced</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
+		<page name="repackconfirm">
+			<template name="page"/>
+
+			<template name="statusbar"/>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="1"/>
+				<placement x="%col1_x_left%" y="%row1_header_y%"/>
+				<text>{@repack_kernel_confirm=Install Kernel?}</text>
+			</text>
+
+			<text style="text_m">
+				<condition var1="tw_repack_kernel" var2="0"/>
+				<placement x="%col1_x_left%" y="%row1_header_y%"/>
+				<text>{@repack_ramdisk_confirm=Install Recovery?}</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row2_y%"/>
+				<text>{@folder=Folder:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row3_y%"/>
+				<text>%tw_zip_location%</text>
+			</text>
+
+			<text style="text_m_accent">
+				<placement x="%indent%" y="%row4_y%"/>
+				<text>{@file=File:}</text>
+			</text>
+
+			<text style="text_m">
+				<placement x="%indent%" y="%row5_y%"/>
+				<text>%tw_file%</text>
+			</text>
+
+			<checkbox>
+				<placement x="%indent%" y="%row7_y%"/>
+				<text>{@repack_backup_first=Back up existing image first}</text>
+				<data variable="tw_repack_backup_first"/>
+			</checkbox>
+
+			<slider>
+				<text>{@swipe_to_install=Swipe to Install}</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=repackimage</action>
+					<action function="set">tw_action_param=/boot</action>
+					<action function="set">tw_action_text1={@installing=Installing...}</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1={@install_complete=Install Complete}</action>
+					<action function="page">action_page</action>
+				</actions>
+			</slider>
+
+			<action>
+				<touch key="back"/>
+				<action function="page">repackselect</action>
+			</action>
+
+			<action>
+				<touch key="home"/>
+				<action function="page">main</action>
+			</action>
+		</page>
+
 		<page name="lock">
 			<background color="%semi_transparent%"/>
 
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
index 9478cd7..1652115 100755
--- a/openrecoveryscript.cpp
+++ b/openrecoveryscript.cpp
@@ -178,7 +178,7 @@
 					strncpy(value2, tok, line_len - remove_nl);
 					DataManager::SetValue(TW_BACKUP_NAME, value2);
 					gui_msg(Msg("backup_folder_set=Backup folder set to '{1}'")(value2));
-					if (PartitionManager.Check_Backup_Name(true) != 0) {
+					if (PartitionManager.Check_Backup_Name(value2, true, true) != 0) {
 						ret_val = 1;
 						continue;
 					}
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 34ba6ae..79068f7 100755
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -602,16 +602,15 @@
 	return NULL;
 }
 
-int TWPartitionManager::Check_Backup_Name(bool Display_Error) {
+int TWPartitionManager::Check_Backup_Name(const std::string& Backup_Name, bool Display_Error, bool Must_Be_Unique) {
 	// Check the backup name to ensure that it is the correct size and contains only valid characters
 	// and that a backup with that name doesn't already exist
 	char backup_name[MAX_BACKUP_NAME_LEN];
 	char backup_loc[255], tw_image_dir[255];
 	int copy_size;
 	int index, cur_char;
-	string Backup_Name, Backup_Loc;
+	string Backup_Loc;
 
-	DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
 	copy_size = Backup_Name.size();
 	// Check size
 	if (copy_size > MAX_BACKUP_NAME_LEN) {
@@ -640,17 +639,20 @@
 		}
 	}
 
-	// Check to make sure that a backup with this name doesn't already exist
-	DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, Backup_Loc);
-	strcpy(backup_loc, Backup_Loc.c_str());
-	sprintf(tw_image_dir,"%s/%s", backup_loc, Backup_Name.c_str());
-	if (TWFunc::Path_Exists(tw_image_dir)) {
-		if (Display_Error)
-			gui_err("backup_name_exists=A backup with that name already exists!");
+	if (Must_Be_Unique) {
+		// Check to make sure that a backup with this name doesn't already exist
+		DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, Backup_Loc);
+		strcpy(backup_loc, Backup_Loc.c_str());
+		sprintf(tw_image_dir,"%s/%s", backup_loc, Backup_Name.c_str());
+		if (TWFunc::Path_Exists(tw_image_dir)) {
+			if (Display_Error)
+				gui_err("backup_name_exists=A backup with that name already exists!");
 
-		return -4;
+			return -4;
+		}
+		// Backup is unique
 	}
-	// No problems found, return 0
+	// No problems found
 	return 0;
 }
 
@@ -2210,6 +2212,22 @@
 				Partition_List->push_back(part);
 			}
 		}
+		if (DataManager::GetIntValue("tw_has_repack_tools") != 0 && DataManager::GetIntValue("tw_has_boot_slots") != 0) {
+			TWPartition* boot = Find_Partition_By_Path("/boot");
+			if (boot) {
+				// Allow flashing kernels and ramdisks
+				struct PartitionList repack_ramdisk;
+				repack_ramdisk.Display_Name = gui_lookup("install_twrp_ramdisk", "Install Recovery Ramdisk");
+				repack_ramdisk.Mount_Point = "/repack_ramdisk";
+				repack_ramdisk.selected = 0;
+				Partition_List->push_back(repack_ramdisk);
+				/*struct PartitionList repack_kernel; For now let's leave repacking kernels under advanced only
+				repack_kernel.Display_Name = gui_lookup("install_kernel", "Install Kernel");
+				repack_kernel.Mount_Point = "/repack_kernel";
+				repack_kernel.selected = 0;
+				Partition_List->push_back(repack_kernel);*/
+			}
+		}
 	} else {
 		LOGERR("Unknown list type '%s' requested for TWPartitionManager::Get_Partition_List\n", ListType.c_str());
 	}
@@ -2512,6 +2530,21 @@
 		}
 	}
 
+	DataManager::GetValue("tw_flash_partition", Flash_List);
+	Repack_Type repack = REPLACE_NONE;
+	if (Flash_List == "/repack_ramdisk;") {
+		repack = REPLACE_RAMDISK;
+	} else if (Flash_List == "/repack_kernel;") {
+		repack = REPLACE_KERNEL;
+	}
+	if (repack != REPLACE_NONE) {
+		Repack_Options_struct Repack_Options;
+		Repack_Options.Type = repack;
+		Repack_Options.Disable_Verity = false;
+		Repack_Options.Disable_Force_Encrypt = false;
+		Repack_Options.Backup_First = DataManager::GetIntValue("tw_repack_backup_first") != 0;
+		return Repack_Images(full_filename, Repack_Options);
+	}
 	PartitionSettings part_settings;
 	part_settings.Backup_Folder = path;
 	unsigned long long total_bytes = TWFunc::Get_File_Size(full_filename);
@@ -2519,9 +2552,7 @@
 	part_settings.progress = &progress;
 	part_settings.adbbackup = false;
 	part_settings.PM_Method = PM_RESTORE;
-
 	gui_msg("calc_restore=Calculating restore details...");
-	DataManager::GetValue("tw_flash_partition", Flash_List);
 	if (!Flash_List.empty()) {
 		end_pos = Flash_List.find(";", start_pos);
 		while (end_pos != string::npos && start_pos < Flash_List.size()) {
@@ -2995,3 +3026,134 @@
 	if (sysfs_entries.size() > 0)
 		Coldboot_Scan(&sysfs_entries, "/sys/block", 0);
 }
+
+bool TWPartitionManager::Prepare_Empty_Folder(const std::string& Folder) {
+	if (TWFunc::Path_Exists(Folder))
+		TWFunc::removeDir(Folder, false);
+	return TWFunc::Recursive_Mkdir(Folder);
+}
+
+bool TWPartitionManager::Prepare_Repack(TWPartition* Part, const std::string& Temp_Folder_Destination, const bool Create_Backup, const std::string& Backup_Name) {
+	if (!Part) {
+		LOGERR("Partition was null!\n");
+		return false;
+	}
+	if (!Prepare_Empty_Folder(Temp_Folder_Destination))
+		return false;
+	std::string target_image = Temp_Folder_Destination + "boot.img";
+	PartitionSettings part_settings;
+	part_settings.Part = Part;
+	if (Create_Backup) {
+		if (Check_Backup_Name(Backup_Name, true, false) != 0)
+			return false;
+		DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, part_settings.Backup_Folder);
+		part_settings.Backup_Folder = part_settings.Backup_Folder + "/" + TWFunc::Get_Current_Date() + " " + Backup_Name + "/";
+		if (!TWFunc::Recursive_Mkdir(part_settings.Backup_Folder))
+			return false;
+	} else
+		part_settings.Backup_Folder = Temp_Folder_Destination;
+	part_settings.adbbackup = false;
+	part_settings.generate_digest = false;
+	part_settings.generate_md5 = false;
+	part_settings.PM_Method = PM_BACKUP;
+	part_settings.progress = NULL;
+	pid_t not_a_pid = 0;
+	if (!Part->Backup(&part_settings, &not_a_pid))
+		return false;
+	std::string backed_up_image = part_settings.Backup_Folder;
+	backed_up_image += Part->Backup_FileName;
+	target_image = Temp_Folder_Destination + "boot.img";
+	if (Create_Backup) {
+		std::string source = part_settings.Backup_Folder + Part->Backup_FileName;
+		if (TWFunc::copy_file(source, target_image, 0644) != 0) {
+			LOGERR("Failed to copy backup file '%s' to temp folder target '%s'\n", source.c_str(), target_image.c_str());
+			return false;
+		}
+	} else {
+		if (rename(backed_up_image.c_str(), target_image.c_str()) != 0) {
+			LOGERR("Failed to rename '%s' to '%s'\n", backed_up_image.c_str(), target_image.c_str());
+			return false;
+		}
+	}
+	return Prepare_Repack(target_image, Temp_Folder_Destination, false, false);
+}
+
+bool TWPartitionManager::Prepare_Repack(const std::string& Source_Path, const std::string& Temp_Folder_Destination, const bool Copy_Source, const bool Create_Destination) {
+	if (Create_Destination) {
+		if (!Prepare_Empty_Folder(Temp_Folder_Destination))
+			return false;
+	}
+	if (Copy_Source) {
+		std::string destination = Temp_Folder_Destination + "/boot.img";
+		if (TWFunc::copy_file(Source_Path, destination, 0644))
+			return false;
+	}
+	std::string command = "cd " + Temp_Folder_Destination + " && /sbin/magiskboot --unpack -h " + Source_Path;
+	if (TWFunc::Exec_Cmd(command) != 0) {
+		LOGINFO("Error unpacking %s!\n", Source_Path.c_str());
+		gui_msg(Msg(msg::kError, "unpack_error=Error unpacking image."));
+		return false;
+	}
+	return true;
+}
+
+bool TWPartitionManager::Repack_Images(const std::string& Target_Image, const struct Repack_Options_struct& Repack_Options) {
+	if (!TWFunc::Path_Exists("/sbin/magiskboot")) {
+		LOGERR("Image repacking tool not present in this TWRP build!");
+		return false;
+	}
+	DataManager::SetProgress(0);
+	TWPartition* part = PartitionManager.Find_Partition_By_Path("/boot");
+	if (part)
+		gui_msg(Msg("unpacking_image=Unpacking {1}...")(part->Display_Name));
+	else {
+		gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/boot"));
+		return false;
+	}
+	if (!PartitionManager.Prepare_Repack(part, REPACK_ORIG_DIR, Repack_Options.Backup_First, gui_lookup("repack", "Repack")))
+		return false;
+	DataManager::SetProgress(.25);
+	gui_msg(Msg("unpacking_image=Unpacking {1}...")(Target_Image));
+	if (!PartitionManager.Prepare_Repack(Target_Image, REPACK_NEW_DIR, true))
+		return false;
+	DataManager::SetProgress(.5);
+	gui_msg(Msg("repacking_image=Repacking {1}...")(part->Display_Name));
+	std::string path = REPACK_NEW_DIR;
+	if (Repack_Options.Type == REPLACE_KERNEL) {
+		// When we replace the kernel, what we really do is copy the boot partition ramdisk into the new image's folder
+		if (TWFunc::copy_file(REPACK_ORIG_DIR "ramdisk.cpio", REPACK_NEW_DIR "ramdisk.cpio", 0644)) {
+			LOGERR("Failed to copy ramdisk\n");
+			return false;
+		}
+	} else if (Repack_Options.Type == REPLACE_RAMDISK) {
+		// Repack the ramdisk
+		if (TWFunc::copy_file(REPACK_NEW_DIR "ramdisk.cpio", REPACK_ORIG_DIR "ramdisk.cpio", 0644)) {
+			LOGERR("Failed to copy ramdisk\n");
+			return false;
+		}
+		path = REPACK_ORIG_DIR;
+	} else {
+		LOGERR("Invalid repacking options specified\n");
+		return false;
+	}
+	if (Repack_Options.Disable_Verity)
+		LOGERR("Disabling verity is not implemented yet\n");
+	if (Repack_Options.Disable_Force_Encrypt)
+		LOGERR("Disabling force encrypt is not implemented yet\n");
+	std::string command = "cd " + path + " && /sbin/magiskboot --repack " + path + "boot.img";
+	if (TWFunc::Exec_Cmd(command) != 0) {
+		gui_msg(Msg(msg::kError, "repack_error=Error repacking image."));
+		return false;
+	}
+	DataManager::SetProgress(.75);
+	std::string file = "new-boot.img";
+	DataManager::SetValue("tw_flash_partition", "/boot;");
+	if (!PartitionManager.Flash_Image(path, file)) {
+		LOGINFO("Error flashing new image\n");
+		return false;
+	}
+	DataManager::SetProgress(1);
+	TWFunc::removeDir(REPACK_ORIG_DIR, false);
+	TWFunc::removeDir(REPACK_NEW_DIR, false);
+	return true;
+}
diff --git a/partitions.hpp b/partitions.hpp
index c124457..4071b94 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -29,6 +29,9 @@
 
 #define MAX_FSTAB_LINE_LENGTH 2048
 
+#define REPACK_ORIG_DIR "/tmp/repackorig/"
+#define REPACK_NEW_DIR "/tmp/repacknew/"
+
 using namespace std;
 
 // BasePartition is used for overriding so we can run custom, device
@@ -72,6 +75,19 @@
 	char* fstab_line;
 };
 
+enum Repack_Type {
+	REPLACE_NONE = 0,
+	REPLACE_RAMDISK = 1,
+	REPLACE_KERNEL = 2,
+};
+
+struct Repack_Options_struct {
+	Repack_Type Type;
+	bool Backup_First;
+	bool Disable_Verity;
+	bool Disable_Force_Encrypt;
+};
+
 enum PartitionManager_Op {                                                    // PartitionManager Restore Mode for Raw_Read_Write()
 	PM_BACKUP = 0,
 	PM_RESTORE = 1,
@@ -300,7 +316,7 @@
 	int Mount_Settings_Storage(bool Display_Error);                           // Mounts the settings file storage location (usually internal)
 	TWPartition* Find_Partition_By_Path(const string& Path);                  // Returns a pointer to a partition based on path
 	TWPartition* Find_Partition_By_Block_Device(const string& Block_Device);  // Returns a pointer to a partition based on block device
-	int Check_Backup_Name(bool Display_Error);                                // Checks the current backup name to ensure that it is valid
+	int Check_Backup_Name(const std::string& Backup_Name, bool Display_Error, bool Must_Be_Unique); // Checks the current backup name to ensure that it is valid and optionally that a backup with that name doesn't already exist
 	int Run_Backup(bool adbbackup);                                           // Initiates a backup in the current storage
 	int Run_Restore(const string& Restore_Name);                              // Restores a backup
 	bool Write_ADB_Stream_Header(uint64_t partition_count);                   // Write ADB header over twrpbu FIFO
@@ -361,6 +377,9 @@
 	void read_uevent();                                                       // Reads uevent data into a buffer
 	void close_uevent();                                                      // Closes the uevent netlink socket
 	void Add_Partition(TWPartition* Part);                                    // Adds a new partition to the Partitions vector
+	bool Prepare_Repack(TWPartition* Part, const std::string& Temp_Folder_Destination, const bool Create_Backup, const std::string& Backup_Name); // Prepares an image for repacking by unpacking it to the temp folder destination
+	bool Prepare_Repack(const std::string& Source_Path, const std::string& Temp_Folder_Destination, const bool Copy_Source, const bool Create_Destination = true); // Prepares an image for repacking by unpacking it to the temp folder destination
+	bool Repack_Images(const std::string& Target_Image, const struct Repack_Options_struct& Repack_Options); // Repacks the boot image with a new kernel or a new ramdisk
 
 private:
 	void Setup_Settings_Storage_Partition(TWPartition* Part);                 // Sets up settings storage
@@ -373,6 +392,7 @@
 	void Post_Decrypt(const string& Block_Device);                            // Completes various post-decrypt tasks
 	void Coldboot_Scan(std::vector<string> *sysfs_entries, const string& Path, int depth); // Scans subfolders to find matches to the paths stored in sysfs_entries so we can trigger the uevent system to "re-add" devices
 	void Coldboot();                                                          // Starts the scan of the /sys/block folder
+	bool Prepare_Empty_Folder(const std::string& Folder);                     // Creates an empty folder at Folder. If the folder already exists, the folder is deleted, then created
 	pid_t mtppid;
 	bool mtp_was_enabled;
 	int mtp_write_fd;
diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk
index 0b0fa86..4d67e5b 100644
--- a/prebuilt/Android.mk
+++ b/prebuilt/Android.mk
@@ -558,3 +558,12 @@
         endif
     endif
 endif
+
+ifeq ($(TW_INCLUDE_REPACKTOOLS), true)
+    ifeq ($(wildcard external/magisk-prebuilt/Android.mk),)
+        $(warning Magisk repacking tools not found!)
+        $(warning Please place https://github.com/TeamWin/external_magisk-prebuilt)
+        $(warning into external/magisk-prebuilt)
+        $(error magiskboot prebuilts not present; exiting)
+    endif
+endif
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index 28a0f0c..5b618e2 100755
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -879,10 +879,11 @@
 		space_check = Backup_Name.substr(Backup_Name.size() - 1, 1);
 	}
 	replace(Backup_Name.begin(), Backup_Name.end(), ' ', '_');
-	DataManager::SetValue(TW_BACKUP_NAME, Backup_Name);
-	if (PartitionManager.Check_Backup_Name(false) != 0) {
-		LOGINFO("Auto generated backup name '%s' contains invalid characters, using date instead.\n", Backup_Name.c_str());
+	if (PartitionManager.Check_Backup_Name(Backup_Name, false, true) != 0) {
+		LOGINFO("Auto generated backup name '%s' is not valid, using date instead.\n", Backup_Name.c_str());
 		DataManager::SetValue(TW_BACKUP_NAME, Get_Current_Date());
+	} else {
+		DataManager::SetValue(TW_BACKUP_NAME, Backup_Name);
 	}
 }