Functional Nandroid
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index 2c39a78..76c39af 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -29,8 +29,8 @@
                        "wipe data/factory reset",
                        "wipe cache partition",
                        "install zip from sdcard",
-                       "toggle signature verification",
-                       "toggle script asserts",
+                       "backup",
+                       "restore",
                        NULL };
 
 int device_toggle_display(volatile char* key_pressed, int key_code) {
diff --git a/extendedcommands.c b/extendedcommands.c
index 4ef23f7..c491d84 100644
--- a/extendedcommands.c
+++ b/extendedcommands.c
@@ -28,6 +28,7 @@
 
 int signature_check_enabled = 1;
 int script_assert_enabled = 1;
+static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
 
 void
 toggle_signature_check()
@@ -42,12 +43,68 @@
     ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled");
 }
 
-void show_choose_zip_menu()
+void install_zip(const char* packagefilepath)
 {
-    static char* headers[] = { "Choose a zip or press POWER to return",
-			       "",
-			       NULL };
-    
+    ui_print("\n-- Installing: %s\n", packagefilepath);
+    set_sdcard_update_bootloader_message();
+    int status = install_package(packagefilepath);
+    if (status != INSTALL_SUCCESS) {
+        ui_set_background(BACKGROUND_ICON_ERROR);
+        ui_print("Installation aborted.\n");
+    } else if (!ui_text_visible()) {
+        return;  // reboot if logs aren't visible
+    } else {
+        if (firmware_update_pending()) {
+            ui_print("\nReboot via menu to complete\n"
+                     "installation.\n");
+        } else {
+            ui_print("\nInstall from sdcard complete.\n");
+        }
+    }
+}
+
+char* INSTALL_MENU_ITEMS[] = {  "apply sdcard:update.zip",
+                                "choose zip from sdcard",
+                                "toggle signature verification",
+                                "toggle script asserts",
+                                NULL };
+#define ITEM_APPLY_SDCARD     0
+#define ITEM_CHOOSE_ZIP       1
+#define ITEM_SIG_CHECK        2
+#define ITEM_ASSERTS          3
+
+void show_install_update_menu()
+{
+    static char* headers[] = {  "Apply update from .zip file on SD card",
+                                "",
+                                NULL 
+    };
+    for (;;)
+    {
+        int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0);
+        switch (chosen_item)
+        {
+            case ITEM_ASSERTS:
+                toggle_script_asserts();
+                break;
+            case ITEM_SIG_CHECK:
+                toggle_signature_check();
+                break;
+            case ITEM_APPLY_SDCARD:
+                install_zip(SDCARD_PACKAGE_FILE);
+                break;
+            case ITEM_CHOOSE_ZIP:
+                show_choose_zip_menu();
+                break;
+            default:
+                return;
+        }
+        
+    }
+}
+
+char* choose_file_menu(const char* directory, const char* extension, const char* headers[])
+{
     char path[PATH_MAX] = "";
     DIR *dir;
     struct dirent *de;
@@ -56,83 +113,126 @@
     char** files;
     char** list;
 
+    dir = opendir(directory);
+    if (dir == NULL) {
+        ui_print("Couldn't open directory.\n");
+        return NULL;
+    }
+  
+    const int extension_length = strlen(extension);
+  
+    while ((de=readdir(dir)) != NULL) {
+        if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) {
+            total++;
+        }
+    }
+
+    if (total==0) {
+        ui_print("No files found.\n");
+        if(closedir(dir) < 0) {
+            LOGE("Failed to close directory.");
+        }
+        return NULL;
+    }
+
+    files = (char**) malloc((total+1)*sizeof(char*));
+    files[total]=NULL;
+
+    list = (char**) malloc((total+1)*sizeof(char*));
+    list[total]=NULL;
+
+    rewinddir(dir);
+
+    i = 0;
+    while ((de = readdir(dir)) != NULL) {
+        if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) {
+            files[i] = (char*) malloc(strlen(directory)+strlen(de->d_name)+1);
+            strcpy(files[i], directory);
+            strcat(files[i], de->d_name);
+
+            list[i] = (char*) malloc(strlen(de->d_name)+1);
+            strcpy(list[i], de->d_name);
+
+            i++;
+        }
+    }
+
+    if (closedir(dir) <0) {
+        LOGE("Failure closing directory.");
+        return NULL;
+    }
+
+    int chosen_item = get_menu_selection(headers, list, 1);
+    static char ret[PATH_MAX];
+    strcpy(ret, files[chosen_item]);
+    return ret;
+}
+
+void show_choose_zip_menu()
+{
     if (ensure_root_path_mounted("SDCARD:") != 0) {
         LOGE ("Can't mount /sdcard\n");
         return;
     }
 
-    dir = opendir("/sdcard");
-    if (dir == NULL) {
-        LOGE("Couldn't open /sdcard");
+    static char* headers[] = {  "Choose a zip to apply",
+                                "",
+                                NULL 
+    };
+    
+    char* file = choose_file_menu("/sdcard/", ".zip", headers);
+    if (file == NULL)
+        return;
+    char sdcard_package_file[1024];
+    strcpy(sdcard_package_file, "SDCARD:");
+    strcat(sdcard_package_file,  file + strlen("/sdcard/"));
+    install_zip(sdcard_package_file);
+}
+
+void do_nandroid_backup()
+{
+    ui_print("Performing backup...\n");
+    system("/sbin/nandroid-mobile.sh backup");
+    ui_print("Backup complete.\n");
+}
+
+void show_nandroid_restore_menu()
+{
+    if (ensure_root_path_mounted("SDCARD:") != 0) {
+        LOGE ("Can't mount /sdcard\n");
         return;
     }
+
+    static char* headers[] = {  "Choose an image to restore",
+                                "",
+                                NULL 
+    };
     
-    const char *extension = ".zip";
-    const int extension_length = strlen(extension);
-    
-    while ((de=readdir(dir)) != NULL) {
-        if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) {
-            total++;
-	      }
+    system("cat /proc/cmdline | sed 's/.*serialno=//' | cut -d' ' -f1 > /.deviceid");
+    FILE *deviceIdFile = fopen(".deviceid", "r");
+    char deviceId[256];
+    if (deviceIdFile == NULL)
+    {
+        ui_print("Unable to retrieve device id.\n");
+        return;
     }
-
-    if (total==0) {
-        LOGE("No tar archives found\n");
-        if(closedir(dir) < 0) {
-            LOGE("Failed to close directory /sdcard");
-            return;
-	      }
+    int readCount = fread(deviceId, 1, sizeof(deviceId), deviceIdFile);
+    if (readCount == 0)
+    {
+        ui_print("Unable to retrieve device id.\n");
+        return;
     }
-    else {
-        files = (char**) malloc((total+1)*sizeof(char*));
-        files[total]=NULL;
+    deviceId[readCount - 1] = NULL;
+    fclose(deviceIdFile);
+    char backupDirectory[PATH_MAX];
+    sprintf(backupDirectory, "/sdcard/nandroid/%s/", deviceId);
 
-        list = (char**) malloc((total+1)*sizeof(char*));
-        list[total]=NULL;
-	
-        rewinddir(dir);
-
-        i = 0;
-        while ((de = readdir(dir)) != NULL) {
-            if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) {
-                files[i] = (char*) malloc(strlen("/sdcard/")+strlen(de->d_name)+1);
-                strcpy(files[i], "/sdcard/");
-                strcat(files[i], de->d_name);
-
-                list[i] = (char*) malloc(strlen(de->d_name)+1);
-                strcpy(list[i], de->d_name);
-
-                i++;
-            }
-        }
-
-        if (closedir(dir) <0) {
-            LOGE("Failure closing directory /sdcard\n");
-            return;
-        }
-
-        int chosen_item = get_menu_selection(headers, list, 1);
-        if (chosen_item >= 0 && chosen_item != GO_BACK) {
-          char sdcard_package_file[1024];
-          strcpy(sdcard_package_file, "SDCARD:");
-          strcat(sdcard_package_file, files[chosen_item] + strlen("/sdcard"));
-
-          ui_print("\n-- Install from sdcard...\n");
-          set_sdcard_update_bootloader_message();
-          int status = install_package(sdcard_package_file);
-          if (status != INSTALL_SUCCESS) {
-              ui_set_background(BACKGROUND_ICON_ERROR);
-              ui_print("Installation aborted.\n");
-          } else if (!ui_text_visible()) {
-              return;  // reboot if logs aren't visible
-          } else {
-              if (firmware_update_pending()) {
-                  ui_print("\nReboot via menu to complete\n"
-                           "installation.\n");
-              } else {
-                  ui_print("\nInstall from sdcard complete.\n");
-              }
-          }
-        }
-    }
+    char* file = choose_file_menu(backupDirectory, "", headers);
+    if (file == NULL)
+        return;
+    char* command[PATH_MAX];
+    sprintf(command, "nandroid-mobile.sh restore %s", file);
+    ui_print("Performing restore...\n");
+    system(command);
+    ui_print("Restore complete.\n");
 }
\ No newline at end of file
diff --git a/extendedcommands.h b/extendedcommands.h
index 2729de2..00dff0f 100644
--- a/extendedcommands.h
+++ b/extendedcommands.h
@@ -14,4 +14,10 @@
 get_allow_toggle_display();
 
 int
-ui_get_show_menu();
\ No newline at end of file
+ui_get_show_menu();
+
+void
+do_nandroid_backup();
+
+void
+show_nandroid_restore_menu();
\ No newline at end of file
diff --git a/recovery.c b/recovery.c
index e398393..a88c696 100644
--- a/recovery.c
+++ b/recovery.c
@@ -440,14 +440,14 @@
                     }
                 }
                 break;
-            case ITEM_SIG_CHECK:
-                toggle_signature_check();
-                break;
-            case ITEM_ASSERTS:
-                toggle_script_asserts();
-                break;
             case ITEM_INSTALL_ZIP:
-                show_choose_zip_menu();
+                show_install_update_menu();
+                break;
+            case ITEM_BACKUP:
+                do_nandroid_backup();
+                break;
+            case ITEM_RESTORE:
+                show_nandroid_restore_menu();
                 break;
         }
     }
diff --git a/recovery_ui.h b/recovery_ui.h
index 4d3d3e2..2964dfc 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -68,8 +68,8 @@
 #define ITEM_WIPE_DATA       2
 #define ITEM_WIPE_CACHE      3
 #define ITEM_INSTALL_ZIP     4
-#define ITEM_SIG_CHECK       5
-#define ITEM_ASSERTS         6
+#define ITEM_BACKUP          5
+#define ITEM_RESTORE         6
 
 // Header text to display above the main menu.
 extern char* MENU_HEADERS[];