Support extracting images from full OTAs

Test: python3 simulate_ota.py your_ota.zip
Change-Id: I99b58d19b6db5da9b51d63b17ca603a1a232cada
diff --git a/scripts/simulate_ota.py b/scripts/simulate_ota.py
index 6349979..e508f50 100644
--- a/scripts/simulate_ota.py
+++ b/scripts/simulate_ota.py
@@ -41,12 +41,14 @@
       with open(os.path.join(zip_file_path, entry_name), "rb") as fp:
         shutil.copyfileobj(fp, out_fp)
 
+
 def is_sparse_image(filepath):
   with open(filepath, 'rb') as fp:
     # Magic for android sparse image format
     # https://source.android.com/devices/bootloader/images
     return fp.read(4) == b'\x3A\xFF\x26\xED'
 
+
 def extract_img(zip_archive, img_name, output_path):
   entry_name = "IMAGES/" + img_name + ".img"
   extract_file(zip_archive, entry_name, output_path)
@@ -55,14 +57,19 @@
     subprocess.check_output(["simg2img", output_path, raw_img_path])
     os.rename(raw_img_path, output_path)
 
-def run_ota(source, target, payload_path, tempdir):
+
+def run_ota(source, target, payload_path, tempdir, output_dir):
   """Run an OTA on host side"""
   payload = update_payload.Payload(payload_path)
   payload.Init()
-  if zipfile.is_zipfile(source):
+  if source and zipfile.is_zipfile(source):
     source = zipfile.ZipFile(source)
-  if zipfile.is_zipfile(target):
+  if target and zipfile.is_zipfile(target):
     target = zipfile.ZipFile(target)
+  source_exist = source and (isinstance(
+      source, zipfile.ZipFile) or os.path.exists(source))
+  target_exist = target and (isinstance(
+      target, zipfile.ZipFile) or os.path.exists(target))
 
   old_partitions = []
   new_partitions = []
@@ -71,10 +78,15 @@
     name = part.partition_name
     old_image = os.path.join(tempdir, "source_" + name + ".img")
     new_image = os.path.join(tempdir, "target_" + name + ".img")
-    print("Extracting source image for", name)
-    extract_img(source, name, old_image)
-    print("Extracting target image for", name)
-    extract_img(target, name, new_image)
+    if part.HasField("old_partition_info"):
+      assert source_exist, \
+          "source target file must point to a valid zipfile or directory " + \
+          source
+      print("Extracting source image for", name)
+      extract_img(source, name, old_image)
+    if target_exist:
+      print("Extracting target image for", name)
+      extract_img(target, name, new_image)
 
     old_partitions.append(old_image)
     scratch_image_name = new_image + ".actual"
@@ -87,15 +99,23 @@
   partition_names = [
       part.partition_name for part in payload.manifest.partitions
   ]
+  if (payload.manifest.partial_update):
+    delta_generator_args.append("--is_partial_update")
+  if payload.is_incremental:
+    delta_generator_args.append("--old_partitions=" + ":".join(old_partitions))
   delta_generator_args.append("--partition_names=" + ":".join(partition_names))
-  delta_generator_args.append("--old_partitions=" + ":".join(old_partitions))
   delta_generator_args.append("--new_partitions=" + ":".join(new_partitions))
 
   subprocess.check_output(delta_generator_args)
 
   valid = True
+  if not target_exist:
+    for part in new_partitions:
+      print("Output written to", part)
+      shutil.copy(part, output_dir)
+    return
   for (expected_part, actual_part, part_name) in \
-      zip(expected_new_partitions, new_partitions, partition_names):
+          zip(expected_new_partitions, new_partitions, partition_names):
     if filecmp.cmp(expected_part, actual_part):
       print("Partition `{}` is valid".format(part_name))
     else:
@@ -114,11 +134,16 @@
   parser.add_argument(
       "--source",
       help="Target file zip for the source build",
-      required=True, nargs=1)
+      required=False)
   parser.add_argument(
       "--target",
       help="Target file zip for the target build",
-      required=True, nargs=1)
+      required=False)
+  parser.add_argument(
+      "-o",
+      dest="output_dir",
+      help="Output directory to put all images, current directory by default"
+  )
   parser.add_argument(
       "payload",
       help="payload.bin for the OTA package, or a zip of OTA package itself",
@@ -126,11 +151,6 @@
   args = parser.parse_args()
   print(args)
 
-  assert os.path.exists(args.source[0]), \
-    "source target file must point to a valid zipfile or directory"
-  assert os.path.exists(args.target[0]), \
-    "target path must point to a valid zipfile or directory"
-
   # pylint: disable=no-member
   with tempfile.TemporaryDirectory() as tempdir:
     payload_path = args.payload[0]
@@ -139,8 +159,12 @@
         payload_entry_name = 'payload.bin'
         zfp.extract(payload_entry_name, tempdir)
         payload_path = os.path.join(tempdir, payload_entry_name)
-
-    run_ota(args.source[0], args.target[0], payload_path, tempdir)
+    if args.output_dir is None:
+      args.output_dir = "."
+    if not os.path.exists(args.output_dir):
+      os.makedirs(args.output_dir, exist_ok=True)
+    assert os.path.isdir(args.output_dir)
+    run_ota(args.source, args.target, payload_path, tempdir, args.output_dir)
 
 
 if __name__ == '__main__':