+#define GIBIBYTE_MIN_NBYTES 10000000000ULL
+#define MEBIBYTE_MIN_NBYTES 10000000ULL
+#define KIBIBYTE_MIN_NBYTES 10000ULL
+
+static unsigned
+get_unit(uint64_t total_bytes, const tchar **name_ret)
+{
+ if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
+ *name_ret = T("GiB");
+ return 30;
+ } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
+ *name_ret = T("MiB");
+ return 20;
+ } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
+ *name_ret = T("KiB");
+ return 10;
+ } else {
+ *name_ret = T("bytes");
+ return 0;
+ }
+}
+
+/* Progress callback function passed to various wimlib functions. */
+static int
+imagex_progress_func(enum wimlib_progress_msg msg,
+ const union wimlib_progress_info *info)
+{
+ unsigned percent_done;
+ unsigned unit_shift;
+ const tchar *unit_name;
+ if (imagex_be_quiet)
+ return 0;
+ switch (msg) {
+ case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
+ unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
+ percent_done = TO_PERCENT(info->write_streams.completed_bytes,
+ info->write_streams.total_bytes);
+ if (info->write_streams.completed_streams == 0) {
+ const tchar *data_type;
+
+ data_type = get_data_type(info->write_streams.compression_type);
+ tprintf(T("Writing %"TS" data using %u thread%"TS"\n"),
+ data_type, info->write_streams.num_threads,
+ (info->write_streams.num_threads == 1) ? T("") : T("s"));
+ }
+ tprintf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
+ "written (%u%% done)"),
+ info->write_streams.completed_bytes >> unit_shift,
+ unit_name,
+ info->write_streams.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
+ tputchar(T('\n'));
+ break;
+ case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
+ tprintf(T("Scanning \"%"TS"\""), info->scan.source);
+ if (*info->scan.wim_target_path) {
+ tprintf(T(" (loading as WIM path: "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\")...\n"),
+ info->scan.wim_target_path);
+ } else {
+ tprintf(T(" (loading as root of WIM image)...\n"));
+ }
+ break;
+ case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
+ if (info->scan.excluded)
+ tprintf(T("Excluding \"%"TS"\" from capture\n"), info->scan.cur_path);
+ else
+ tprintf(T("Scanning \"%"TS"\"\n"), info->scan.cur_path);
+ break;
+ /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/
+ /*break;*/
+ case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
+ unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
+ percent_done = TO_PERCENT(info->integrity.completed_bytes,
+ info->integrity.total_bytes);
+ tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
+ "of %"PRIu64" %"TS" (%u%%) done"),
+ info->integrity.filename,
+ info->integrity.completed_bytes >> unit_shift,
+ unit_name,
+ info->integrity.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ if (info->integrity.completed_bytes == info->integrity.total_bytes)
+ tputchar(T('\n'));
+ break;
+ case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
+ unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
+ percent_done = TO_PERCENT(info->integrity.completed_bytes,
+ info->integrity.total_bytes);
+ tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
+ "of %"PRIu64" %"TS" (%u%%) done"),
+ info->integrity.completed_bytes >> unit_shift,
+ unit_name,
+ info->integrity.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ if (info->integrity.completed_bytes == info->integrity.total_bytes)
+ tputchar(T('\n'));
+ break;
+ case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
+ tprintf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
+ "to %"TS" \"%"TS"\"\n"),
+ info->extract.image,
+ info->extract.image_name,
+ info->extract.wimfile_name,
+ ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
+ T("NTFS volume") : T("directory")),
+ info->extract.target);
+ break;
+ case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
+ tprintf(T("Extracting "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\" from image %d (\"%"TS"\") "
+ "in \"%"TS"\" to \"%"TS"\"\n"),
+ info->extract.extract_root_wim_source_path,
+ info->extract.image,
+ info->extract.image_name,
+ info->extract.wimfile_name,
+ info->extract.target);
+ break;
+ /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/
+ /*tprintf(T("Applying directory structure to %"TS"\n"),*/
+ /*info->extract.target);*/
+ /*break;*/
+ case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
+ percent_done = TO_PERCENT(info->extract.completed_bytes,
+ info->extract.total_bytes);
+ unit_shift = get_unit(info->extract.total_bytes, &unit_name);
+ tprintf(T("\rExtracting files: "
+ "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
+ info->extract.completed_bytes >> unit_shift,
+ unit_name,
+ info->extract.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ if (info->extract.completed_bytes >= info->extract.total_bytes)
+ tputchar(T('\n'));
+ break;
+ case WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY:
+ tprintf(T("%"TS"\n"), info->extract.cur_path);
+ break;
+ case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
+ if (info->extract.extract_root_wim_source_path[0] == T('\0'))
+ tprintf(T("Setting timestamps on all extracted files...\n"));
+ break;
+ case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
+ if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
+ tprintf(T("Unmounting NTFS volume \"%"TS"\"...\n"),
+ info->extract.target);
+ }
+ break;
+ case WIMLIB_PROGRESS_MSG_JOIN_STREAMS:
+ percent_done = TO_PERCENT(info->join.completed_bytes,
+ info->join.total_bytes);
+ unit_shift = get_unit(info->join.total_bytes, &unit_name);
+ tprintf(T("Writing resources from part %u of %u: "
+ "%"PRIu64 " %"TS" of %"PRIu64" %"TS" (%u%%) written\n"),
+ (info->join.completed_parts == info->join.total_parts) ?
+ info->join.completed_parts : info->join.completed_parts + 1,
+ info->join.total_parts,
+ info->join.completed_bytes >> unit_shift,
+ unit_name,
+ info->join.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ break;
+ case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
+ percent_done = TO_PERCENT(info->split.completed_bytes,
+ info->split.total_bytes);
+ unit_shift = get_unit(info->split.total_bytes, &unit_name);
+ tprintf(T("Writing \"%"TS"\": %"PRIu64" %"TS" of "
+ "%"PRIu64" %"TS" (%u%%) written\n"),
+ info->split.part_name,
+ info->split.completed_bytes >> unit_shift,
+ unit_name,
+ info->split.total_bytes >> unit_shift,
+ unit_name,
+ percent_done);
+ break;
+ case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
+ if (info->split.completed_bytes == info->split.total_bytes) {
+ tprintf(T("Finished writing %u split WIM parts\n"),
+ info->split.cur_part_number);
+ }
+ break;
+ case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
+ switch (info->update.command->op) {
+ case WIMLIB_UPDATE_OP_DELETE:
+ tprintf(T("Deleted WIM path "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+ info->update.command->delete.wim_path);
+ break;
+ case WIMLIB_UPDATE_OP_RENAME:
+ tprintf(T("Renamed WIM path "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\" => "
+ "\""WIMLIB_WIM_PATH_SEPARATOR_STRING "%"TS"\"\n"),
+ info->update.command->rename.wim_source_path,
+ info->update.command->rename.wim_target_path);
+ break;
+ case WIMLIB_UPDATE_OP_ADD:
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ fflush(stdout);
+ return 0;
+}
+
+/* Open all the split WIM parts that correspond to a file glob.
+ *
+ * @first_part specifies the first part of the split WIM and it may be either
+ * included or omitted from the glob. */
+static int
+open_swms_from_glob(const tchar *swm_glob,
+ const tchar *first_part,
+ int open_flags,
+ WIMStruct ***additional_swms_ret,
+ unsigned *num_additional_swms_ret)